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
8 #include "include/core/SkAlphaType.h"
9 #include "include/core/SkBitmap.h"
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkColor.h"
12 #include "include/core/SkColorPriv.h"
13 #include "include/core/SkColorSpace.h"
14 #include "include/core/SkColorType.h"
15 #include "include/core/SkImageInfo.h"
16 #include "include/core/SkMatrix.h"
17 #include "include/core/SkPaint.h"
18 #include "include/core/SkPixmap.h"
19 #include "include/core/SkPoint.h"
20 #include "include/core/SkRect.h"
21 #include "include/core/SkRefCnt.h"
22 #include "include/core/SkScalar.h"
23 #include "include/core/SkShader.h"
24 #include "include/core/SkSize.h"
25 #include "include/core/SkSurface.h"
26 #include "include/core/SkSurfaceProps.h"
27 #include "include/core/SkTileMode.h"
28 #include "include/core/SkTypes.h"
29 #include "include/effects/SkGradientShader.h"
30 #include "include/gpu/GpuTypes.h"
31 #include "include/gpu/ganesh/GrDirectContext.h"
32 #include "include/gpu/ganesh/SkSurfaceGanesh.h"
33 #include "include/gpu/ganesh/mock/GrMockTypes.h"
34 #include "include/private/base/SkTemplates.h"
35 #include "include/private/base/SkTo.h"
36 #include "include/private/gpu/ganesh/GrTypesPriv.h"
37 #include "src/base/SkTLazy.h"
38 #include "src/gpu/ganesh/GrColorInfo.h"
39 #include "src/gpu/ganesh/GrFPArgs.h"
40 #include "src/gpu/ganesh/GrFragmentProcessors.h"
41 #include "src/shaders/SkShaderBase.h"
42 #include "tests/CtsEnforcement.h"
43 #include "tests/Test.h"
44
45 #include <cstdint>
46 #include <cstring>
47 #include <string>
48
49 // #if defined(SK_GRAPHITE)
50 // #include "include/gpu/graphite/Context.h"
51 // #include "include/gpu/graphite/Surface.h"
52 // #endif
53
54 struct GrContextOptions;
55
56 using namespace skia_private;
57
58 // https://code.google.com/p/chromium/issues/detail?id=448299
59 // Giant (inverse) matrix causes overflow when converting/computing using 32.32
60 // Before the fix, we would assert (and then crash).
test_big_grad(skiatest::Reporter * reporter)61 static void test_big_grad(skiatest::Reporter* reporter) {
62 const SkColor colors[] = { SK_ColorRED, SK_ColorBLUE };
63 const SkPoint pts[] = {{ 15, 14.7112684f }, { 0.709064007f, 12.6108112f }};
64 SkPaint paint;
65 paint.setShader(SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkTileMode::kClamp));
66
67 SkBitmap bm;
68 bm.allocN32Pixels(2000, 1);
69 SkCanvas c(bm);
70
71 const SkScalar affine[] = {
72 1.06608627e-06f, 4.26434525e-07f, 6.2855f, 2.6611f, 273.4393f, 244.0046f
73 };
74 SkMatrix matrix;
75 matrix.setAffine(affine);
76 c.concat(matrix);
77
78 c.drawPaint(paint);
79 }
80
81 struct GradRec {
82 int fColorCount;
83 const SkColor* fColors;
84 const SkScalar* fPos;
85 const SkPoint* fPoint; // 2
86 const SkScalar* fRadius; // 2
87 SkTileMode fTileMode;
88
gradCheckGradRec89 void gradCheck(skiatest::Reporter* reporter,
90 const sk_sp<SkShader>& shader,
91 SkShaderBase::GradientInfo* info,
92 SkShaderBase::GradientType gt,
93 const SkMatrix& localMatrix = SkMatrix::I()) const {
94 AutoTMalloc<SkColor> colorStorage(fColorCount);
95 AutoTMalloc<SkScalar> posStorage(fColorCount);
96
97 info->fColorCount = fColorCount;
98 info->fColors = colorStorage;
99 info->fColorOffsets = posStorage.get();
100 SkMatrix shaderLocalMatrix;
101 REPORTER_ASSERT(reporter, as_SB(shader)->asGradient(info, &shaderLocalMatrix) == gt);
102 REPORTER_ASSERT(reporter, shaderLocalMatrix == localMatrix);
103
104 REPORTER_ASSERT(reporter, info->fColorCount == fColorCount);
105 REPORTER_ASSERT(reporter,
106 !memcmp(info->fColors, fColors, fColorCount * sizeof(SkColor)));
107 REPORTER_ASSERT(reporter,
108 !memcmp(info->fColorOffsets, fPos, fColorCount * sizeof(SkScalar)));
109 REPORTER_ASSERT(reporter, fTileMode == (SkTileMode)info->fTileMode);
110 }
111 };
112
113
none_gradproc(skiatest::Reporter * reporter,const GradRec &,const GradRec &)114 static void none_gradproc(skiatest::Reporter* reporter, const GradRec&, const GradRec&) {
115 sk_sp<SkShader> s(SkShaders::Empty());
116 REPORTER_ASSERT(reporter, SkShaderBase::GradientType::kNone == as_SB(s)->asGradient());
117 }
118
color_gradproc(skiatest::Reporter * reporter,const GradRec & rec,const GradRec &)119 static void color_gradproc(skiatest::Reporter* reporter, const GradRec& rec, const GradRec&) {
120 sk_sp<SkShader> s(SkShaders::Color(rec.fColors[0]));
121 REPORTER_ASSERT(reporter, SkShaderBase::GradientType::kNone == as_SB(s)->asGradient());
122 }
123
linear_gradproc(skiatest::Reporter * reporter,const GradRec & buildRec,const GradRec & checkRec)124 static void linear_gradproc(skiatest::Reporter* reporter, const GradRec& buildRec,
125 const GradRec& checkRec) {
126 sk_sp<SkShader> s(SkGradientShader::MakeLinear(buildRec.fPoint, buildRec.fColors, buildRec.fPos,
127 buildRec.fColorCount, buildRec.fTileMode));
128
129 SkShaderBase::GradientInfo info;
130 checkRec.gradCheck(reporter, s, &info, SkShaderBase::GradientType::kLinear);
131 REPORTER_ASSERT(reporter, !memcmp(info.fPoint, checkRec.fPoint, 2 * sizeof(SkPoint)));
132 }
133
radial_gradproc(skiatest::Reporter * reporter,const GradRec & buildRec,const GradRec & checkRec)134 static void radial_gradproc(skiatest::Reporter* reporter, const GradRec& buildRec,
135 const GradRec& checkRec) {
136 sk_sp<SkShader> s(SkGradientShader::MakeRadial(buildRec.fPoint[0], buildRec.fRadius[0],
137 buildRec.fColors, buildRec.fPos,
138 buildRec.fColorCount, buildRec.fTileMode));
139
140 SkShaderBase::GradientInfo info;
141 checkRec.gradCheck(reporter, s, &info, SkShaderBase::GradientType::kRadial);
142 REPORTER_ASSERT(reporter, info.fPoint[0] == checkRec.fPoint[0]);
143 REPORTER_ASSERT(reporter, info.fRadius[0] == checkRec.fRadius[0]);
144 }
145
sweep_gradproc(skiatest::Reporter * reporter,const GradRec & buildRec,const GradRec & checkRec)146 static void sweep_gradproc(skiatest::Reporter* reporter, const GradRec& buildRec,
147 const GradRec& checkRec) {
148 sk_sp<SkShader> s(SkGradientShader::MakeSweep(buildRec.fPoint[0].fX, buildRec.fPoint[0].fY,
149 buildRec.fColors, buildRec.fPos,
150 buildRec.fColorCount));
151
152 SkShaderBase::GradientInfo info;
153 checkRec.gradCheck(reporter, s, &info, SkShaderBase::GradientType::kSweep);
154 REPORTER_ASSERT(reporter, info.fPoint[0] == checkRec.fPoint[0]);
155 }
156
conical_gradproc(skiatest::Reporter * reporter,const GradRec & buildRec,const GradRec & checkRec)157 static void conical_gradproc(skiatest::Reporter* reporter, const GradRec& buildRec,
158 const GradRec& checkRec) {
159 sk_sp<SkShader> s(SkGradientShader::MakeTwoPointConical(buildRec.fPoint[0],
160 buildRec.fRadius[0],
161 buildRec.fPoint[1],
162 buildRec.fRadius[1],
163 buildRec.fColors,
164 buildRec.fPos,
165 buildRec.fColorCount,
166 buildRec.fTileMode));
167
168 SkShaderBase::GradientInfo info;
169 checkRec.gradCheck(reporter, s, &info, SkShaderBase::GradientType::kConical);
170 REPORTER_ASSERT(reporter, !memcmp(info.fPoint, checkRec.fPoint, 2 * sizeof(SkPoint)));
171 REPORTER_ASSERT(reporter, !memcmp(info.fRadius, checkRec.fRadius, 2 * sizeof(SkScalar)));
172 }
173
linear_gradproc_matrix(skiatest::Reporter * reporter,const GradRec & buildRec,const GradRec & checkRec)174 static void linear_gradproc_matrix(skiatest::Reporter* reporter, const GradRec& buildRec,
175 const GradRec& checkRec) {
176 SkMatrix localMatrix = SkMatrix::RotateDeg(45, {100, 100});
177 sk_sp<SkShader> s(SkGradientShader::MakeLinear(buildRec.fPoint, buildRec.fColors, buildRec.fPos,
178 buildRec.fColorCount, buildRec.fTileMode,
179 /*flags=*/0,
180 &localMatrix));
181
182 SkShaderBase::GradientInfo info;
183 checkRec.gradCheck(reporter, s, &info, SkShaderBase::GradientType::kLinear, localMatrix);
184 REPORTER_ASSERT(reporter, !memcmp(info.fPoint, checkRec.fPoint, 2 * sizeof(SkPoint)));
185
186 // Same but using a local matrix wrapper.
187 s = SkGradientShader::MakeLinear(buildRec.fPoint, buildRec.fColors, buildRec.fPos,
188 buildRec.fColorCount, buildRec.fTileMode);
189 s = s->makeWithLocalMatrix(localMatrix);
190 checkRec.gradCheck(reporter, s, &info, SkShaderBase::GradientType::kLinear, localMatrix);
191 REPORTER_ASSERT(reporter, !memcmp(info.fPoint, checkRec.fPoint, 2 * sizeof(SkPoint)));
192 }
193
194 // Ensure that repeated color gradients behave like drawing a single color
TestConstantGradient(skiatest::Reporter *)195 static void TestConstantGradient(skiatest::Reporter*) {
196 const SkPoint pts[] = {
197 { 0, 0 },
198 { SkIntToScalar(10), 0 }
199 };
200 SkColor colors[] = { SK_ColorBLUE, SK_ColorBLUE };
201 const SkScalar pos[] = { 0, SK_Scalar1 };
202 SkPaint paint;
203 paint.setShader(SkGradientShader::MakeLinear(pts, colors, pos, 2, SkTileMode::kClamp));
204 SkBitmap outBitmap;
205 outBitmap.allocN32Pixels(10, 1);
206 SkCanvas canvas(outBitmap);
207 canvas.drawPaint(paint);
208 for (int i = 0; i < 10; i++) {
209 // The following is commented out because it currently fails
210 // Related bug: https://code.google.com/p/skia/issues/detail?id=1098
211
212 // REPORTER_ASSERT(reporter, SK_ColorBLUE == outBitmap.getColor(i, 0));
213 }
214 }
215
216 typedef void (*GradProc)(skiatest::Reporter* reporter, const GradRec&, const GradRec&);
217
TestGradientShaders(skiatest::Reporter * reporter)218 static void TestGradientShaders(skiatest::Reporter* reporter) {
219 static const SkColor gColors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE };
220 static const SkScalar gPos[] = { 0, SK_ScalarHalf, SK_Scalar1 };
221 static const SkPoint gPts[] = {
222 { 0, 0 },
223 { SkIntToScalar(10), SkIntToScalar(20) }
224 };
225 static const SkScalar gRad[] = { SkIntToScalar(1), SkIntToScalar(2) };
226
227 GradRec rec;
228 rec.fColorCount = std::size(gColors);
229 rec.fColors = gColors;
230 rec.fPos = gPos;
231 rec.fPoint = gPts;
232 rec.fRadius = gRad;
233 rec.fTileMode = SkTileMode::kClamp;
234
235 static const GradProc gProcs[] = {
236 none_gradproc,
237 color_gradproc,
238 linear_gradproc,
239 linear_gradproc_matrix,
240 radial_gradproc,
241 sweep_gradproc,
242 conical_gradproc,
243 };
244
245 for (size_t i = 0; i < std::size(gProcs); ++i) {
246 gProcs[i](reporter, rec, rec);
247 }
248 }
249
test_nearly_vertical(skiatest::Reporter * reporter)250 static void test_nearly_vertical(skiatest::Reporter* reporter) {
251 auto surface(SkSurfaces::Raster(SkImageInfo::MakeN32Premul(200, 200)));
252
253 const SkPoint pts[] = {{ 100, 50 }, { 100.0001f, 50000 }};
254 const SkColor colors[] = { SK_ColorBLACK, SK_ColorWHITE };
255 const SkScalar pos[] = { 0, 1 };
256 SkPaint paint;
257 paint.setShader(SkGradientShader::MakeLinear(pts, colors, pos, 2, SkTileMode::kClamp));
258
259 surface->getCanvas()->drawPaint(paint);
260 }
261
test_vertical(skiatest::Reporter * reporter)262 static void test_vertical(skiatest::Reporter* reporter) {
263 auto surface(SkSurfaces::Raster(SkImageInfo::MakeN32Premul(200, 200)));
264
265 const SkPoint pts[] = {{ 100, 50 }, { 100, 50 }};
266 const SkColor colors[] = { SK_ColorBLACK, SK_ColorWHITE };
267 const SkScalar pos[] = { 0, 1 };
268 SkPaint paint;
269 paint.setShader(SkGradientShader::MakeLinear(pts, colors, pos, 2, SkTileMode::kClamp));
270
271 surface->getCanvas()->drawPaint(paint);
272 }
273
274 // A linear gradient interval can, due to numerical imprecision (likely in the divide)
275 // finish an interval with the final fx not landing outside of [p0...p1].
276 // The old code had an assert which this test triggered.
277 // We now explicitly clamp the resulting fx value.
test_linear_fuzz(skiatest::Reporter * reporter)278 static void test_linear_fuzz(skiatest::Reporter* reporter) {
279 auto surface(SkSurfaces::Raster(SkImageInfo::MakeN32Premul(1300, 630)));
280
281 const SkPoint pts[] = {{ 179.5f, -179.5f }, { 1074.5f, 715.5f }};
282 const SkColor colors[] = { SK_ColorBLACK, SK_ColorWHITE, SK_ColorBLACK, SK_ColorWHITE };
283 const SkScalar pos[] = {0, 0.200000003f, 0.800000012f, 1 };
284
285 SkPaint paint;
286 paint.setShader(SkGradientShader::MakeLinear(pts, colors, pos, 4, SkTileMode::kClamp));
287
288 SkRect r = {0, 83, 1254, 620};
289 surface->getCanvas()->drawRect(r, paint);
290 }
291
292 // https://bugs.chromium.org/p/skia/issues/detail?id=5023
293 // We should still shade pixels for which the radius is exactly 0.
test_two_point_conical_zero_radius(skiatest::Reporter * reporter)294 static void test_two_point_conical_zero_radius(skiatest::Reporter* reporter) {
295 auto surface(SkSurfaces::Raster(SkImageInfo::MakeN32Premul(5, 5)));
296 surface->getCanvas()->clear(SK_ColorRED);
297
298 const SkColor colors[] = { SK_ColorGREEN, SK_ColorBLUE };
299 SkPaint p;
300 p.setShader(SkGradientShader::MakeTwoPointConical(
301 SkPoint::Make(2.5f, 2.5f), 0,
302 SkPoint::Make(3.0f, 3.0f), 10,
303 colors, nullptr, std::size(colors), SkTileMode::kClamp));
304 surface->getCanvas()->drawPaint(p);
305
306 // r == 0 for the center pixel.
307 // verify that we draw it (no red bleed)
308 SkPMColor centerPMColor;
309 surface->readPixels(SkImageInfo::MakeN32Premul(1, 1), ¢erPMColor, sizeof(SkPMColor), 2, 2);
310 REPORTER_ASSERT(reporter, SkGetPackedR32(centerPMColor) == 0);
311 }
312
313 // http://crbug.com/599458
test_clamping_overflow(skiatest::Reporter *)314 static void test_clamping_overflow(skiatest::Reporter*) {
315 SkPaint p;
316 const SkColor colors[] = { SK_ColorRED, SK_ColorGREEN };
317 const SkPoint pts1[] = { SkPoint::Make(1001, 1000001), SkPoint::Make(1000.99f, 1000000) };
318
319 p.setShader(SkGradientShader::MakeLinear(pts1, colors, nullptr, 2, SkTileMode::kClamp));
320
321 sk_sp<SkSurface> surface(SkSurfaces::Raster(SkImageInfo::MakeN32Premul(50, 50)));
322 surface->getCanvas()->scale(100, 100);
323 surface->getCanvas()->drawPaint(p);
324
325 const SkPoint pts2[] = { SkPoint::Make(10000.99f, 1000000), SkPoint::Make(10001, 1000001) };
326 p.setShader(SkGradientShader::MakeLinear(pts2, colors, nullptr, 2, SkTileMode::kClamp));
327 surface->getCanvas()->drawPaint(p);
328
329 // Passes if we don't trigger asserts.
330 }
331
332 // http://crbug.com/636194
test_degenerate_linear(skiatest::Reporter *)333 static void test_degenerate_linear(skiatest::Reporter*) {
334 SkPaint p;
335 const SkColor colors[] = { SK_ColorRED, SK_ColorGREEN };
336 const SkPoint pts[] = {
337 SkPoint::Make(-46058024627067344430605278824628224.0f, 0),
338 SkPoint::Make(SK_ScalarMax, 0)
339 };
340
341 p.setShader(SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkTileMode::kClamp));
342 sk_sp<SkSurface> surface(SkSurfaces::Raster(SkImageInfo::MakeN32Premul(50, 50)));
343 surface->getCanvas()->drawPaint(p);
344
345 // Passes if we don't trigger asserts.
346 }
347
348 // http://crbug.com/1149216
test_unsorted_degenerate(skiatest::Reporter * r)349 static void test_unsorted_degenerate(skiatest::Reporter* r) {
350 // Passes if a valid solid color is computed for the degenerate gradient
351 // (unsorted positions are fixed during regular gradient construction, so this ensures the
352 // same fixing happens for degenerate gradients as well). If they aren't fixed, this test
353 // case produces a negative alpha, which asserts during SkPMColor4f::isOpaque().
354 const SkColor4f colors[] = { {0.f, 0.f, 0.f, 0.f},
355 {0.00784314f, 0.f, 0.f, 0.0627451f},
356 {0.f, 0.00392157f, 0.f, 0.f} };
357 const SkScalar positions[] = {0.00753367f, 8.54792e-44f, 1.46955e-39f};
358
359 const SkPoint points[] { { 0.f, 0.f }, { 1e-20f, -1e-8f }}; // must be degenerate
360 // Use kMirror to go through average color stop calculation, vs. kClamp which would pick a color
361 sk_sp<SkShader> gradient = SkGradientShader::MakeLinear(points, colors, nullptr, positions, 3,
362 SkTileMode::kMirror);
363
364 // The degenerate gradient shouldn't be null
365 REPORTER_ASSERT(r, SkToBool(gradient));
366 // And it shouldn't crash when creating a fragment processor
367
368 GrColorInfo dstColorInfo(GrColorType::kRGBA_8888, kPremul_SkAlphaType,
369 SkColorSpace::MakeSRGB());
370 SkSurfaceProps props;
371 GrMockOptions options;
372 auto context = GrDirectContext::MakeMock(&options);
373
374 GrFPArgs args(context.get(), &dstColorInfo, props, GrFPArgs::Scope::kDefault);
375 GrFragmentProcessors::Make(gradient.get(), args, SkMatrix::I());
376 }
377
378 // "Interesting" fuzzer values.
test_linear_fuzzer(skiatest::Reporter *)379 static void test_linear_fuzzer(skiatest::Reporter*) {
380 static const SkColor gColors0[] = { 0x30303030, 0x30303030 };
381 static const SkColor gColors1[] = { 0x30303030, 0x30303030, 0x30303030 };
382
383 static const SkScalar gPos1[] = { 0, 0, 1 };
384
385 static const SkScalar gMatrix0[9] = {
386 6.40969056e-10f, 0 , 6.40969056e-10f,
387 0 , 4.42539023e-39f, 6.40969056e-10f,
388 0 , 0 , 1
389 };
390 static const SkScalar gMatrix1[9] = {
391 -2.75294113f , 6.40969056e-10f, 6.40969056e-10f,
392 6.40969056e-10f, 6.40969056e-10f, -3.32810161e+24f,
393 6.40969056e-10f, 6.40969056e-10f, 0
394 };
395 static const SkScalar gMatrix2[9] = {
396 7.93481258e+17f, 6.40969056e-10f, 6.40969056e-10f,
397 6.40969056e-10f, 6.40969056e-10f, 6.40969056e-10f,
398 6.40969056e-10f, 6.40969056e-10f, 0.688235283f
399 };
400 static const SkScalar gMatrix3[9] = {
401 1.89180674e+11f, 6.40969056e-10f, 6.40969056e-10f,
402 6.40969056e-10f, 6.40969056e-10f, 6.40969056e-10f,
403 6.40969056e-10f, 11276.0469f , 8.12524808e+20f
404 };
405
406 static const struct {
407 SkPoint fPts[2];
408 const SkColor* fColors;
409 const SkScalar* fPos;
410 int fCount;
411 SkTileMode fTileMode;
412 uint32_t fFlags;
413 const SkScalar* fLocalMatrix;
414 const SkScalar* fGlobalMatrix;
415 } gConfigs[] = {
416 {
417 {{0, -2.752941f}, {0, 0}},
418 gColors0,
419 nullptr,
420 std::size(gColors0),
421 SkTileMode::kClamp,
422 0,
423 gMatrix0,
424 nullptr
425 },
426 {
427 {{4.42539023e-39f, -4.42539023e-39f}, {9.78041162e-15f, 4.42539023e-39f}},
428 gColors1,
429 gPos1,
430 std::size(gColors1),
431 SkTileMode::kClamp,
432 0,
433 nullptr,
434 gMatrix1
435 },
436 {
437 {{4.42539023e-39f, 6.40969056e-10f}, {6.40969056e-10f, 1.49237238e-19f}},
438 gColors1,
439 gPos1,
440 std::size(gColors1),
441 SkTileMode::kClamp,
442 0,
443 nullptr,
444 gMatrix2
445 },
446 {
447 {{6.40969056e-10f, 6.40969056e-10f}, {6.40969056e-10f, -0.688235283f}},
448 gColors0,
449 nullptr,
450 std::size(gColors0),
451 SkTileMode::kClamp,
452 0,
453 gMatrix3,
454 nullptr
455 },
456 };
457
458 sk_sp<SkColorSpace> srgb = SkColorSpace::MakeSRGB();
459 SkColorSpace* colorSpaces[] = {
460 nullptr, // hits the legacy gradient impl
461 srgb.get(), // triggers 4f/raster-pipeline
462 };
463
464 SkPaint paint;
465
466 for (const SkColorSpace* colorSpace : colorSpaces) {
467 sk_sp<SkSurface> surface = SkSurfaces::Raster(SkImageInfo::Make(
468 100, 100, kN32_SkColorType, kPremul_SkAlphaType, sk_ref_sp(colorSpace)));
469 SkCanvas* canvas = surface->getCanvas();
470
471 for (const auto& config : gConfigs) {
472 SkAutoCanvasRestore acr(canvas, false);
473 SkTLazy<SkMatrix> localMatrix;
474 if (config.fLocalMatrix) {
475 localMatrix.init();
476 localMatrix->set9(config.fLocalMatrix);
477 }
478
479 paint.setShader(SkGradientShader::MakeLinear(config.fPts,
480 config.fColors,
481 config.fPos,
482 config.fCount,
483 config.fTileMode,
484 config.fFlags,
485 localMatrix.getMaybeNull()));
486 if (config.fGlobalMatrix) {
487 SkMatrix m;
488 m.set9(config.fGlobalMatrix);
489 canvas->save();
490 canvas->concat(m);
491 }
492
493 canvas->drawPaint(paint);
494 }
495 }
496 }
497
test_sweep_fuzzer(skiatest::Reporter *)498 static void test_sweep_fuzzer(skiatest::Reporter*) {
499 static const SkColor gColors0[] = { 0x30303030, 0x30303030, 0x30303030 };
500 static const SkScalar gPos0[] = { -47919293023455565225163489280.0f, 0, 1 };
501 static const SkScalar gMatrix0[9] = {
502 1.12116716e-13f, 0 , 8.50489682e+16f,
503 4.1917041e-41f , 3.51369881e-23f, -2.54344271e-26f,
504 9.61111907e+17f, -3.35263808e-29f, -1.35659403e+14f
505 };
506 static const struct {
507 SkPoint fCenter;
508 const SkColor* fColors;
509 const SkScalar* fPos;
510 int fCount;
511 const SkScalar* fGlobalMatrix;
512 } gConfigs[] = {
513 {
514 { 0, 0 },
515 gColors0,
516 gPos0,
517 std::size(gColors0),
518 gMatrix0
519 },
520 };
521
522 sk_sp<SkSurface> surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(100, 100));
523 SkCanvas* canvas = surface->getCanvas();
524 SkPaint paint;
525
526 for (const auto& config : gConfigs) {
527 paint.setShader(SkGradientShader::MakeSweep(config.fCenter.x(),
528 config.fCenter.y(),
529 config.fColors,
530 config.fPos,
531 config.fCount));
532
533 SkAutoCanvasRestore acr(canvas, false);
534 if (config.fGlobalMatrix) {
535 SkMatrix m;
536 m.set9(config.fGlobalMatrix);
537 canvas->save();
538 canvas->concat(m);
539 }
540 canvas->drawPaint(paint);
541 }
542 }
543
544 // Draw a sweep gradient in a translated canvas such that the colors in the center pixels of the
545 // gradient will be evaluated at x = 0. The gradient implementation must not call atan2(y, x) with
546 // x == 0, as this will result in undefined behavior and likely incorrect results.
547 // https://crbug.com/1468916
test_sweep_gradient_zero_x(skiatest::Reporter * reporter,SkSurface * surface)548 void test_sweep_gradient_zero_x(skiatest::Reporter* reporter, SkSurface* surface) {
549 // The gradient drawn has yellow for the first half and blue for the second half, using hard
550 // stops and running clockwise from (1, 0), so we should draw a rectangle with a blue top-half
551 // and yellow bottom-half.
552 constexpr float pts[4] = {0.0f, 0.5f, 0.5f, 1.0f};
553 constexpr SkColor colors[4] = {SK_ColorYELLOW, SK_ColorYELLOW, SK_ColorBLUE, SK_ColorBLUE};
554 SkCanvas* canvas = surface->getCanvas();
555 canvas->save();
556 canvas->translate(2.5f, 2.5f);
557 SkPaint paint;
558 paint.setShader(SkGradientShader::MakeSweep(0.0f, 0.0f, colors, pts, 4));
559 canvas->drawRect(SkRect::MakeXYWH(-2.5f, -2.5f, 5.0f, 5.0f), paint);
560 canvas->restore();
561
562 // Read pixels.
563 SkBitmap bitmap;
564 SkPixmap pixmap;
565 bitmap.allocPixels(surface->imageInfo());
566 SkAssertResult(bitmap.peekPixels(&pixmap));
567 if (!surface->readPixels(pixmap, 0, 0)) {
568 ERRORF(reporter, "readPixels failed");
569 return;
570 }
571
572 // Check the results.
573 SkColor4f topColor = pixmap.getColor4f(2, 0);
574 SkColor4f bottomColor = pixmap.getColor4f(2, 4);
575 REPORTER_ASSERT(reporter, topColor == SkColors::kBlue);
576 REPORTER_ASSERT(reporter, bottomColor == SkColors::kYellow);
577 }
578
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(TestSweepGradientZeroXGanesh,reporter,contextInfo,CtsEnforcement::kApiLevel_V)579 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(TestSweepGradientZeroXGanesh,
580 reporter,
581 contextInfo,
582 CtsEnforcement::kApiLevel_V) {
583 SkImageInfo ii = SkImageInfo::Make(SkISize::Make(5, 5),
584 SkColorType::kRGBA_8888_SkColorType,
585 SkAlphaType::kPremul_SkAlphaType);
586 GrDirectContext* context = contextInfo.directContext();
587 sk_sp<SkSurface> surface = SkSurfaces::RenderTarget(context, skgpu::Budgeted::kYes, ii);
588 test_sweep_gradient_zero_x(reporter, surface.get());
589 }
590
591 // TODO: Fix this bug in Graphite as well.
592 // #if defined(SK_GRAPHITE)
593 // DEF_GRAPHITE_TEST_FOR_RENDERING_CONTEXTS(TestSweepGradientZeroXGraphite, reporter, context,
594 // CtsEnforcement::kNextRelease) {
595 // using namespace skgpu::graphite;
596 // SkImageInfo ii = SkImageInfo::Make(SkISize::Make(5, 5),
597 // SkColorType::kRGBA_8888_SkColorType,
598 // SkAlphaType::kPremul_SkAlphaType);
599 // std::unique_ptr<Recorder> recorder = context->makeRecorder();
600 // sk_sp<SkSurface> surface = SkSurfaces::RenderTarget(recorder.get(), ii);
601 // test_sweep_gradient_zero_x(reporter, surface.get());
602 // }
603 // #endif
604
DEF_TEST(Gradient,reporter)605 DEF_TEST(Gradient, reporter) {
606 TestGradientShaders(reporter);
607 TestConstantGradient(reporter);
608 test_big_grad(reporter);
609 test_nearly_vertical(reporter);
610 test_vertical(reporter);
611 test_linear_fuzz(reporter);
612 test_two_point_conical_zero_radius(reporter);
613 test_clamping_overflow(reporter);
614 test_degenerate_linear(reporter);
615 test_linear_fuzzer(reporter);
616 test_sweep_fuzzer(reporter);
617 test_unsorted_degenerate(reporter);
618 }
619