50#include <initializer_list>
67enum class SavePolicy {
98 const SkIRect& deviceBounds()
const {
return fDeviceBounds; }
99 ClipStack::ClipState expectedState()
const {
return fExpectedState; }
100 const std::vector<ClipStack::Element>& initialElements()
const {
return fElements; }
101 const std::vector<ClipStack::Element>& expectedElements()
const {
return fExpectedElements; }
104 friend class TestCaseBuilder;
108 ClipStack::ClipState expectedState,
109 std::vector<ClipStack::Element> actual,
110 std::vector<ClipStack::Element> expected)
112 , fElements(
std::move(actual))
113 , fDeviceBounds(deviceBounds)
114 , fExpectedElements(
std::move(expected))
115 , fExpectedState(expectedState) {}
117 SkString getTestName(
const std::vector<int>& order, SavePolicy policy)
const;
122 std::pair<SkIRect, bool> getOptimalBounds()
const;
127 std::vector<ClipStack::Element> fElements;
132 std::vector<ClipStack::Element> fExpectedElements;
133 ClipStack::ClipState fExpectedState;
136class ElementsBuilder {
141 ElementsBuilder& localToDevice(
const SkMatrix& m) { fLocalToDevice =
m;
return *
this; }
142 ElementsBuilder& aa() { fAA =
GrAA::kYes;
return *
this; }
143 ElementsBuilder& nonAA() { fAA =
GrAA::kNo;
return *
this; }
150 return this->rect(rect, fLocalToDevice, fAA, fOp);
153 return this->rect(rect, fLocalToDevice, aa, op);
156 fElements->push_back({
GrShape(rect),
m, op, aa});
161 return this->rrect(rrect, fLocalToDevice, fAA, fOp);
164 return this->rrect(rrect, fLocalToDevice, aa, op);
167 fElements->push_back({
GrShape(rrect),
m, op, aa});
172 return this->path(path, fLocalToDevice, fAA, fOp);
175 return this->path(path, fLocalToDevice, aa, op);
178 fElements->push_back({
GrShape(path),
m, op, aa});
183 TestCaseBuilder& finishElements() {
188 friend class TestCaseBuilder;
190 ElementsBuilder(TestCaseBuilder* builder, std::vector<ClipStack::Element>* elements)
192 , fElements(elements) {}
198 TestCaseBuilder* fBuilder;
199 std::vector<ClipStack::Element>* fElements;
202class TestCaseBuilder {
206 ElementsBuilder actual() {
return ElementsBuilder(
this, &fActualElements); }
207 ElementsBuilder expect() {
return ElementsBuilder(
this, &fExpectedElements); }
209 TestCaseBuilder& expectActual() {
210 fExpectedElements = fActualElements;
214 TestCaseBuilder&
state(ClipStack::ClipState
state) {
215 fExpectedState =
state;
221 std::move(fActualElements), std::move(fExpectedElements));
223 fExpectedState = ClipStack::ClipState::kWideOpen;
230 explicit TestCaseBuilder(
const char*
name,
const SkIRect& deviceBounds)
232 , fDeviceBounds(deviceBounds)
233 , fExpectedState(ClipStack::ClipState::kWideOpen) {}
237 ClipStack::ClipState fExpectedState;
239 std::vector<ClipStack::Element> fActualElements;
240 std::vector<ClipStack::Element> fExpectedElements;
243TestCaseBuilder TestCase::Build(
const char*
name,
const SkIRect& deviceBounds) {
244 return TestCaseBuilder(
name, deviceBounds);
247SkString TestCase::getTestName(
const std::vector<int>& order, SavePolicy policy)
const {
252 case SavePolicy::kNever:
253 policyName =
"never";
255 case SavePolicy::kAtStart:
256 policyName =
"start";
258 case SavePolicy::kAtEnd:
261 case SavePolicy::kBetweenEveryOp:
262 policyName =
"between";
267 for (
size_t i = 0; i < order.size(); ++i) {
271 name.appendf(
"%d", order[i]);
277std::pair<SkIRect, bool> TestCase::getOptimalBounds()
const {
278 if (fExpectedState == ClipStack::ClipState::kEmpty) {
282 bool expectOptimal =
true;
284 for (
const ClipStack::Element& e : fExpectedElements) {
292 expectOptimal &=
e.fLocalToDevice.isIdentity();
297 expectOptimal =
false;
298 if (
e.fShape.isRect() &&
e.fLocalToDevice.isIdentity()) {
301 }
else if (
e.fShape.isRRect() &&
e.fLocalToDevice.isIdentity()) {
316 if (
a.fAA !=
b.fAA ||
a.fOp !=
b.fOp ||
a.fLocalToDevice !=
b.fLocalToDevice ||
317 a.fShape.type() !=
b.fShape.type()) {
320 switch(
a.fShape.type()) {
322 return a.fShape.rect() ==
b.fShape.rect();
324 return a.fShape.rrect() ==
b.fShape.rrect();
329 return a.fShape.path().getGenerationID() ==
b.fShape.path().getGenerationID() ||
330 (
a.fShape.convex() &&
332 a.fShape.path() ==
b.fShape.path());
334 SkDEBUGFAIL(
"Shape type not handled by test case yet.");
339void TestCase::run(
const std::vector<int>& order,
342 SkASSERT(fElements.size() == order.size());
344 ClipStack cs(fDeviceBounds, &
SkMatrix::I(),
false);
346 if (policy == SavePolicy::kAtStart) {
350 for (
int i : order) {
351 if (policy == SavePolicy::kBetweenEveryOp) {
354 const ClipStack::Element&
e = fElements[i];
355 switch(
e.fShape.type()) {
357 cs.clipRect(
e.fLocalToDevice,
e.fShape.rect(),
e.fAA,
e.fOp);
360 cs.clipRRect(
e.fLocalToDevice,
e.fShape.rrect(),
e.fAA,
e.fOp);
363 cs.clipPath(
e.fLocalToDevice,
e.fShape.path(),
e.fAA,
e.fOp);
366 SkDEBUGFAIL(
"Shape type not handled by test case yet.");
370 if (policy == SavePolicy::kAtEnd) {
377 "%s, clip state expected %d, actual %d",
378 name.c_str(), (
int) fExpectedState, (
int) cs.clipState());
379 SkIRect actualBounds = cs.getConservativeBounds();
382 std::tie(optimalBounds, expectOptimal) = this->getOptimalBounds();
386 "%s, bounds expected [%d %d %d %d], actual [%d %d %d %d]",
393 "%s, bounds are not conservative, optimal [%d %d %d %d], actual [%d %d %d %d]",
400 size_t matchedElements = 0;
401 for (
const ClipStack::Element&
a : cs) {
403 for (
const ClipStack::Element& e : fExpectedElements) {
404 if (compare_elements(
a, e)) {
412 "%s, unexpected clip element in stack: shape %d, aa %d, op %d",
413 name.c_str(), (
int)
a.fShape.type(), (
int)
a.fAA, (
int)
a.fOp);
414 matchedElements += found ? 1 : 0;
417 "%s, did not match all expected elements: expected %zu but matched only %zu",
418 name.c_str(), fExpectedElements.size(), matchedElements);
421 if (policy == SavePolicy::kAtEnd) {
422 ClipStack::ClipState oldState = cs.clipState();
425 "%s, restoring an empty save record should not change clip state: "
426 "expected %d but got %d",
427 name.c_str(), (
int) oldState, (
int) cs.clipState());
428 }
else if (policy != SavePolicy::kNever) {
429 int restoreCount =
policy == SavePolicy::kAtStart ? 1 : (
int) order.size();
430 for (
int i = 0; i < restoreCount; ++i) {
435 "%s, restore should make stack become wide-open, not %d",
436 name.c_str(), (
int) cs.clipState());
443 int n = (
int)
test.initialElements().size();
444 std::vector<int> order(n);
445 std::vector<int> stack(n);
448 for (
int i = 0; i < n; ++i) {
453 auto runTest = [&]() {
454 static const SavePolicy kPolicies[] = { SavePolicy::kNever, SavePolicy::kAtStart,
455 SavePolicy::kAtEnd, SavePolicy::kBetweenEveryOp };
456 for (
auto policy : kPolicies) {
457 test.run(order, policy, r);
465 static constexpr int kMaxRuns = 720;
469 while (i < n && testRuns < kMaxRuns) {
473 swap(order[0], order[i]);
475 swap(order[stack[i]], order[i]);
506 return make_octagon(r, lr, tb);
509static constexpr SkIRect kDeviceBounds = {0, 0, 100, 100};
520 const char*
name()
const override {
return "NoOp"; }
546 SkRect pixelAligned = {0, 0, 10, 10};
560 .rect(fracRect1).rect(fracRect2)
562 .expect().aa().
intersect().rect(fracIntersect).finishElements()
563 .
state(ClipState::kDeviceRect)
569 .rect(fracRect1).rect(fracRect2)
571 .expect().nonAA().
intersect().rect(fracIntersect).finishElements()
572 .
state(ClipState::kDeviceRect)
576 run_test_case(r, TestCase::Build(
"aligned-aa+nonaa", kDeviceBounds)
578 .aa().rect(pixelAligned).nonAA().rect(fracRect1)
580 .expect().nonAA().
intersect().rect(alignedIntersect).finishElements()
581 .
state(ClipState::kDeviceRect)
585 run_test_case(r, TestCase::Build(
"aa+aligned-nonaa", kDeviceBounds)
587 .aa().rect(fracRect1).nonAA().rect(pixelAligned)
589 .expect().aa().
intersect().rect(alignedIntersect).finishElements()
590 .
state(ClipState::kDeviceRect)
596 .aa().rect(fracRect1).nonAA().rect(fracRect2)
599 .
state(ClipState::kComplex)
608 SkRect r1 = {15.f, 14.f, 23.22f, 58.2f};
612 run_test_case(r, TestCase::Build(
"no-combine", kDeviceBounds)
617 .
state(ClipState::kComplex)
626 SkRect pixelAligned = {0, 0, 10, 10};
639 .actual().aa().
intersect().localToDevice(lm)
640 .rect(fracRect1).rect(fracRect2)
642 .expect().aa().
intersect().localToDevice(lm)
643 .rect(fracIntersect).finishElements()
644 .
state(ClipState::kComplex)
649 .actual().nonAA().
intersect().localToDevice(lm)
650 .rect(fracRect1).rect(fracRect2)
652 .expect().nonAA().
intersect().localToDevice(lm)
653 .rect(fracIntersect).finishElements()
654 .
state(ClipState::kComplex)
660 .aa().rect(pixelAligned).nonAA().rect(fracRect1)
663 .
state(ClipState::kComplex)
684 .
state(ClipState::kDeviceRRect)
693 .
state(ClipState::kDeviceRRect)
699 .aa().rrect(r1).nonAA().rrect(r2)
702 .
state(ClipState::kComplex)
708 .actual().aa().
intersect().localToDevice(lm)
711 .expect().aa().
intersect().localToDevice(lm)
713 .
state(ClipState::kComplex)
715 run_test_case(r, TestCase::Build(
"local-nonaa", kDeviceBounds)
716 .actual().nonAA().
intersect().localToDevice(lm)
719 .expect().nonAA().
intersect().localToDevice(lm)
721 .
state(ClipState::kComplex)
730 SkRect cutTop = {-10, -10, 10, 4};
731 SkRect cutMid = {-10, 3, 10, 7};
734 SkVector cutCorners[4] = {{2.f, 2.f}, {2.f, 2.f}, {0, 0}, {0, 0}};
737 run_test_case(r, TestCase::Build(
"still-rrect", kDeviceBounds)
738 .actual().
intersect().aa().rrect(rrect).rect(cutTop).finishElements()
739 .expect().
intersect().aa().rrect(cutRRect).finishElements()
740 .
state(ClipState::kDeviceRRect)
744 SkRect cutRect = {0, 3, 10, 7};
746 .actual().
intersect().aa().rrect(rrect).rect(cutMid).finishElements()
747 .expect().
intersect().aa().rect(cutRect).finishElements()
748 .
state(ClipState::kDeviceRect)
752 cutRect = {0, 0, 1.5f, 5.f};
753 run_test_case(r, TestCase::Build(
"no-combine", kDeviceBounds)
754 .actual().
intersect().aa().rrect(rrect).rect(cutRect).finishElements()
756 .
state(ClipState::kComplex)
764 SkRect crossesDeviceEdge = {20.f, kDeviceBounds.
fTop - 13.2f,
765 kDeviceBounds.fRight + 15.5f, 30.f};
766 SkRect insideDevice = {20.f, kDeviceBounds.
fTop, kDeviceBounds.fRight, 30.f};
768 run_test_case(r, TestCase::Build(
"device-aa-rect", kDeviceBounds)
769 .actual().
intersect().aa().rect(crossesDeviceEdge).finishElements()
770 .expect().
intersect().aa().rect(insideDevice).finishElements()
771 .
state(ClipState::kDeviceRect)
774 run_test_case(r, TestCase::Build(
"device-nonaa-rect", kDeviceBounds)
775 .actual().
intersect().nonAA().rect(crossesDeviceEdge).finishElements()
776 .expect().
intersect().nonAA().rect(insideDevice).finishElements()
777 .
state(ClipState::kDeviceRect)
785 SkRect crossesDeviceEdge = {20.f, kDeviceBounds.
fTop - 13.2f,
786 kDeviceBounds.fRight + 15.5f, 30.f};
789 run_test_case(r, TestCase::Build(
"device-rrect", kDeviceBounds)
794 .
state(ClipState::kDeviceRRect)
798 run_test_case(r, TestCase::Build(
"device-path", kDeviceBounds)
800 .path(make_octagon(crossesDeviceEdge))
803 .
state(ClipState::kComplex)
814 .actual().path(
empty).finishElements()
815 .
state(ClipState::kEmpty)
820 .actual().path(point).finishElements()
821 .
state(ClipState::kEmpty)
825 line.moveTo({0.f, 0.f});
826 line.lineTo({10.f, 5.f});
828 .actual().path(line).finishElements()
829 .
state(ClipState::kEmpty)
833 SkRect rect = {0.f, 2.f, 10.f, 15.4f};
837 .actual().path(rectPath).finishElements()
838 .expect().rect(rect).finishElements()
839 .
state(ClipState::kDeviceRect)
846 .actual().path(ovalPath).finishElements()
848 .
state(ClipState::kDeviceRRect)
856 .actual().path(rrectPath).finishElements()
857 .expect().rrect(rrect).finishElements()
858 .
state(ClipState::kDeviceRRect)
867 SkRect rect = {5.3f, 62.f, 20.f, 85.f};
868 run_test_case(r, TestCase::Build(
"same-rects", kDeviceBounds)
869 .actual().rect(rect).rect(rect).rect(rect).finishElements()
870 .expect().rect(rect).finishElements()
871 .
state(ClipState::kDeviceRect)
874 lm.
setRotate(30.f, rect.centerX(), rect.centerY());
875 run_test_case(r, TestCase::Build(
"same-local-rects", kDeviceBounds)
876 .actual().localToDevice(lm).rect(rect).rect(rect).rect(rect)
878 .expect().localToDevice(lm).rect(rect).finishElements()
879 .
state(ClipState::kComplex)
884 run_test_case(r, TestCase::Build(
"same-rrects", kDeviceBounds)
885 .actual().rrect(rrect).rrect(rrect).rrect(rrect).finishElements()
886 .expect().rrect(rrect).finishElements()
887 .
state(ClipState::kDeviceRRect)
889 run_test_case(r, TestCase::Build(
"same-local-rrects", kDeviceBounds)
890 .actual().localToDevice(lm).rrect(rrect).rrect(rrect).rrect(rrect)
892 .expect().localToDevice(lm).rrect(rrect).finishElements()
893 .
state(ClipState::kComplex)
897 run_test_case(r, TestCase::Build(
"same-convex", kDeviceBounds)
898 .actual().path(make_octagon(rect)).path(make_octagon(rect))
900 .expect().path(make_octagon(rect)).finishElements()
901 .
state(ClipState::kComplex)
903 run_test_case(r, TestCase::Build(
"same-local-convex", kDeviceBounds)
904 .actual().localToDevice(lm)
905 .path(make_octagon(rect)).path(make_octagon(rect))
907 .expect().localToDevice(lm).path(make_octagon(rect))
909 .
state(ClipState::kComplex)
914 path.moveTo({0.f, 0.f});
915 path.lineTo({20.f, 20.f});
916 path.lineTo({0.f, 20.f});
917 path.lineTo({20.f, 0.f});
921 .actual().path(path).path(path).path(path).finishElements()
922 .expect().path(path).finishElements()
923 .
state(ClipState::kComplex)
925 run_test_case(r, TestCase::Build(
"same-local-path", kDeviceBounds)
926 .actual().localToDevice(lm)
927 .path(path).path(path).path(path).finishElements()
928 .expect().localToDevice(lm).path(path)
930 .
state(ClipState::kComplex)
938 SkRect rect = {0.f, 0.f, 16.f, 17.f};
942 SkPath inverseRectPath = rectPath;
945 SkPath complexPath = make_octagon(rect);
946 SkPath inverseComplexPath = complexPath;
950 run_test_case(r, TestCase::Build(
"inverse-rect-intersect", kDeviceBounds)
951 .actual().aa().
intersect().path(inverseRectPath).finishElements()
952 .expect().aa().
difference().rect(rect).finishElements()
953 .
state(ClipState::kComplex)
957 run_test_case(r, TestCase::Build(
"inverse-rect-difference", kDeviceBounds)
958 .actual().aa().
difference().path(inverseRectPath).finishElements()
959 .expect().aa().
intersect().rect(rect).finishElements()
960 .
state(ClipState::kDeviceRect)
964 run_test_case(r, TestCase::Build(
"inverse-path-intersect", kDeviceBounds)
965 .actual().aa().
intersect().path(inverseComplexPath).finishElements()
966 .expect().aa().
difference().path(complexPath).finishElements()
967 .
state(ClipState::kComplex)
971 run_test_case(r, TestCase::Build(
"inverse-path-difference", kDeviceBounds)
972 .actual().aa().
difference().path(inverseComplexPath).finishElements()
973 .expect().aa().
intersect().path(complexPath).finishElements()
974 .
state(ClipState::kComplex)
982 SkRect offscreenRect = {kDeviceBounds.
fRight + 10.f, kDeviceBounds.fTop + 20.f,
983 kDeviceBounds.fRight + 40.f, kDeviceBounds.fTop + 60.f};
987 SkPath offscreenPath = make_octagon(offscreenRect);
990 run_test_case(r, TestCase::Build(
"intersect-combo", kDeviceBounds)
993 .rrect(offscreenRRect)
996 .
state(ClipState::kEmpty)
998 run_test_case(r, TestCase::Build(
"intersect-rect", kDeviceBounds)
1000 .rect(offscreenRect)
1002 .
state(ClipState::kEmpty)
1004 run_test_case(r, TestCase::Build(
"intersect-rrect", kDeviceBounds)
1006 .rrect(offscreenRRect)
1008 .
state(ClipState::kEmpty)
1010 run_test_case(r, TestCase::Build(
"intersect-path", kDeviceBounds)
1012 .path(offscreenPath)
1014 .
state(ClipState::kEmpty)
1018 run_test_case(r, TestCase::Build(
"difference-combo", kDeviceBounds)
1020 .rect(offscreenRect)
1021 .rrect(offscreenRRect)
1022 .path(offscreenPath)
1024 .
state(ClipState::kWideOpen)
1026 run_test_case(r, TestCase::Build(
"difference-rect", kDeviceBounds)
1028 .rect(offscreenRect)
1030 .
state(ClipState::kWideOpen)
1032 run_test_case(r, TestCase::Build(
"difference-rrect", kDeviceBounds)
1034 .rrect(offscreenRRect)
1036 .
state(ClipState::kWideOpen)
1038 run_test_case(r, TestCase::Build(
"difference-path", kDeviceBounds)
1040 .path(offscreenPath)
1042 .
state(ClipState::kWideOpen)
1051 run_test_case(r, TestCase::Build(
"empty-intersect", kDeviceBounds)
1053 .
state(ClipState::kEmpty)
1057 run_test_case(r, TestCase::Build(
"empty-difference", kDeviceBounds)
1059 .
state(ClipState::kWideOpen)
1063 run_test_case(r, TestCase::Build(
"noop-difference", kDeviceBounds)
1066 .expect().
difference().rrect(rrect).finishElements()
1067 .
state(ClipState::kComplex)
1075 SkRect rightSide = {50.f, -10.f, 2.f * kDeviceBounds.
fRight, kDeviceBounds.fBottom + 10.f};
1076 SkRect clipped = rightSide;
1079 run_test_case(r, TestCase::Build(
"difference-cut", kDeviceBounds)
1080 .actual().nonAA().
difference().rect(rightSide).finishElements()
1081 .expect().nonAA().
difference().rect(clipped).finishElements()
1082 .
state(ClipState::kComplex)
1090 SkRect intR1 = {0.f, 0.f, 30.f, 30.f};
1091 SkRect intR2 = {15.f, 15.f, 45.f, 45.f};
1092 SkRect intCombo = {15.f, 15.f, 30.f, 30.f};
1093 SkRect diff = {20.f, 6.f, 50.f, 50.f};
1095 run_test_case(r, TestCase::Build(
"cross-diff-combine", kDeviceBounds)
1103 .
state(ClipState::kComplex)
1113 static constexpr int kNumOps = 16;
1115 auto b = TestCase::Build(
"many-paths-difference", kDeviceBounds);
1116 SkRect d = {0.f, 0.f, 12.f, 12.f};
1117 for (
int i = 0; i < kNumOps; ++i) {
1121 if (
d.fRight > kDeviceBounds.fRight) {
1124 d.offset(0.f, 15.f);
1129 .state(ClipState::kComplex)
1132 b = TestCase::Build(
"many-paths-intersect", kDeviceBounds);
1133 d = {0.f, 0.f, 12.f, 12.f};
1134 for (
int i = 0; i < kNumOps; ++i) {
1136 d.offset(0.01f, 0.01f);
1140 .state(ClipState::kComplex)
1149 SkRect rect = {0, 0, 20, 20};
1150 run_test_case(r, TestCase::Build(
"device-rect", kDeviceBounds)
1151 .actual().
intersect().aa().rect(rect).finishElements()
1153 .
state(ClipState::kDeviceRect)
1158 run_test_case(r, TestCase::Build(
"unaligned-rect", kDeviceBounds)
1159 .actual().localToDevice(lm).
intersect().aa().rect(rect)
1162 .
state(ClipState::kComplex)
1166 run_test_case(r, TestCase::Build(
"diff-rect", kDeviceBounds)
1167 .actual().
difference().aa().rect(rect).finishElements()
1169 .
state(ClipState::kComplex)
1178 SkRect rect = {0, 0, 20, 20};
1180 run_test_case(r, TestCase::Build(
"device-rrect", kDeviceBounds)
1181 .actual().
intersect().aa().rrect(rrect).finishElements()
1183 .
state(ClipState::kDeviceRRect)
1188 run_test_case(r, TestCase::Build(
"unaligned-rrect", kDeviceBounds)
1189 .actual().localToDevice(lm).
intersect().aa().rrect(rrect)
1192 .
state(ClipState::kComplex)
1196 run_test_case(r, TestCase::Build(
"diff-rrect", kDeviceBounds)
1197 .actual().
difference().aa().rrect(rrect).finishElements()
1199 .
state(ClipState::kComplex)
1214 SkRect rect = {0.f, 0.f, 10.f, 10.f};
1220 .state(ClipState::kDeviceRect)
1232 .
state(ClipState::kDeviceRRect)
1237 .actual().
intersect().localToDevice(lm).path(make_octagon(rect))
1240 .
state(ClipState::kComplex)
1253 SkRect rect = {0.f, 0.f, 10.f, 10.f};
1259 .state(ClipState::kDeviceRect)
1266 run_test_case(r, TestCase::Build(
"r90+rrect", kDeviceBounds)
1271 .
state(ClipState::kDeviceRRect)
1276 .actual().
intersect().localToDevice(lm).path(make_octagon(rect))
1279 .
state(ClipState::kComplex)
1288 SkRect rect = {15.f, 15.f, 30.f, 30.f};
1290 SkPath bigPath = make_octagon(rect.makeOutset(10.f, 10.f), 5.f, 5.f);
1293 run_test_case(r, TestCase::Build(
"convex+rect-intersect", kDeviceBounds)
1294 .actual().aa().
intersect().rect(rect).path(bigPath).finishElements()
1295 .expect().aa().
intersect().rect(rect).finishElements()
1296 .
state(ClipState::kDeviceRect)
1298 run_test_case(r, TestCase::Build(
"convex+rrect-intersect", kDeviceBounds)
1299 .actual().aa().
intersect().rrect(rrect).path(bigPath).finishElements()
1300 .expect().aa().
intersect().rrect(rrect).finishElements()
1301 .
state(ClipState::kDeviceRRect)
1305 run_test_case(r, TestCase::Build(
"convex+rect-difference", kDeviceBounds)
1306 .actual().aa().
difference().rect(rect).path(bigPath).finishElements()
1307 .expect().aa().
difference().path(bigPath).finishElements()
1308 .
state(ClipState::kComplex)
1310 run_test_case(r, TestCase::Build(
"convex+rrect-difference", kDeviceBounds)
1311 .actual().aa().
difference().rrect(rrect).path(bigPath)
1313 .expect().aa().
difference().path(bigPath).finishElements()
1314 .
state(ClipState::kComplex)
1318 run_test_case(r, TestCase::Build(
"convex-diff+rect-int", kDeviceBounds)
1321 .
state(ClipState::kEmpty)
1323 run_test_case(r, TestCase::Build(
"convex-diff+rrect-int", kDeviceBounds)
1326 .
state(ClipState::kEmpty)
1330 run_test_case(r, TestCase::Build(
"convex-int+rect-diff", kDeviceBounds)
1334 .
state(ClipState::kComplex)
1336 run_test_case(r, TestCase::Build(
"convex-int+rrect-diff", kDeviceBounds)
1340 .
state(ClipState::kComplex)
1350 SkRect bigR = {-20.f, -20.f, 20.f, 20.f};
1354 SkRect smR = {-10.f, -10.f, 10.f, 10.f};
1358 run_test_case(r, TestCase::Build(
"rect-rect-ii", kDeviceBounds)
1364 .
state(ClipState::kComplex)
1366 run_test_case(r, TestCase::Build(
"rrect-rrect-ii", kDeviceBounds)
1372 .
state(ClipState::kComplex)
1374 run_test_case(r, TestCase::Build(
"rect-rrect-ii", kDeviceBounds)
1380 .
state(ClipState::kComplex)
1382 run_test_case(r, TestCase::Build(
"rrect-rect-ii", kDeviceBounds)
1388 .
state(ClipState::kComplex)
1392 run_test_case(r, TestCase::Build(
"rect-rect-dd", kDeviceBounds)
1398 .
state(ClipState::kComplex)
1400 run_test_case(r, TestCase::Build(
"rrect-rrect-dd", kDeviceBounds)
1406 .
state(ClipState::kComplex)
1408 run_test_case(r, TestCase::Build(
"rect-rrect-dd", kDeviceBounds)
1414 .
state(ClipState::kComplex)
1416 run_test_case(r, TestCase::Build(
"rrect-rect-dd", kDeviceBounds)
1422 .
state(ClipState::kComplex)
1426 run_test_case(r, TestCase::Build(
"rectD-rectI", kDeviceBounds)
1430 .
state(ClipState::kEmpty)
1432 run_test_case(r, TestCase::Build(
"rrectD-rrectI", kDeviceBounds)
1436 .
state(ClipState::kEmpty)
1438 run_test_case(r, TestCase::Build(
"rectD-rrectI", kDeviceBounds)
1442 .
state(ClipState::kEmpty)
1444 run_test_case(r, TestCase::Build(
"rrectD-rectI", kDeviceBounds)
1448 .
state(ClipState::kEmpty)
1452 run_test_case(r, TestCase::Build(
"rectI+rectD", kDeviceBounds)
1457 .
state(ClipState::kComplex)
1459 run_test_case(r, TestCase::Build(
"rrectI+rrectD", kDeviceBounds)
1464 .
state(ClipState::kComplex)
1466 run_test_case(r, TestCase::Build(
"rrectI+rectD", kDeviceBounds)
1471 .
state(ClipState::kComplex)
1473 run_test_case(r, TestCase::Build(
"rectI+rrectD", kDeviceBounds)
1478 .
state(ClipState::kComplex)
1488 SkRect r1 = {-20.f, -20.f, 20.f, 20.f};
1491 SkRect r2Safe = {-10.f, -10.f, 10.f, 10.f};
1492 SkRect r2Unsafe = {-19.5f, -19.5f, 19.5f, 19.5f};
1495 run_test_case(r, TestCase::Build(
"mixed-outeraa-combine", kDeviceBounds)
1501 .
state(ClipState::kComplex)
1504 run_test_case(r, TestCase::Build(
"mixed-inneraa-combine", kDeviceBounds)
1510 .
state(ClipState::kComplex)
1514 run_test_case(r, TestCase::Build(
"mixed-outeraa-nocombine", kDeviceBounds)
1519 .
state(ClipState::kComplex)
1521 run_test_case(r, TestCase::Build(
"mixed-inneraa-nocombine", kDeviceBounds)
1526 .
state(ClipState::kComplex)
1536 SkPath convex = make_octagon(rect, 10.f, 10.f);
1539 run_test_case(r, TestCase::Build(
"rect-intersect", kDeviceBounds)
1540 .actual().
intersect().rect(rect).finishElements()
1541 .
state(ClipState::kWideOpen)
1543 run_test_case(r, TestCase::Build(
"rrect-intersect", kDeviceBounds)
1544 .actual().
intersect().rrect(rrect).finishElements()
1545 .
state(ClipState::kWideOpen)
1547 run_test_case(r, TestCase::Build(
"convex-intersect", kDeviceBounds)
1548 .actual().
intersect().path(convex).finishElements()
1549 .
state(ClipState::kWideOpen)
1553 run_test_case(r, TestCase::Build(
"rect-difference", kDeviceBounds)
1554 .actual().
difference().rect(rect).finishElements()
1555 .
state(ClipState::kEmpty)
1557 run_test_case(r, TestCase::Build(
"rrect-difference", kDeviceBounds)
1558 .actual().
difference().rrect(rrect).finishElements()
1559 .
state(ClipState::kEmpty)
1561 run_test_case(r, TestCase::Build(
"convex-difference", kDeviceBounds)
1562 .actual().
difference().path(convex).finishElements()
1563 .
state(ClipState::kEmpty)
1572 SkRect rt = {10.f, 10.f, 20.f, 20.f};
1578 .actual().aa().
intersect().rect(rt).rrect(rr).path(p).finishElements()
1579 .
state(ClipState::kEmpty)
1584 .actual().nonAA().
difference().rect(rt).rrect(rr).path(p)
1587 .
state(ClipState::kComplex)
1595 .expect().aa().
intersect().rect(rt).finishElements()
1596 .
state(ClipState::kDeviceRect)
1604 .expect().aa().
intersect().rrect(rr).finishElements()
1605 .
state(ClipState::kDeviceRRect)
1613 .expect().aa().
intersect().path(p).finishElements()
1614 .
state(ClipState::kComplex)
1621 static constexpr float kN = 10.f;
1622 static constexpr float kR = kN / 3.f;
1625 static const SkRect kTL = {0.f, 0.f, 2.f * kN, 2.f * kN};
1626 static const SkRect kTR = {kN, 0.f, 3.f * kN, 2.f * kN};
1627 static const SkRect kBL = {0.f, kN, 2.f * kN, 3.f * kN};
1628 static const SkRect kBR = {kN, kN, 3.f * kN, 3.f * kN};
1634 for (
int opBits = 6; opBits < 16; ++opBits) {
1641 auto b = TestCase::Build(
name.c_str(), kDeviceBounds);
1642 for (
int i = 0; i < 4; ++i) {
1665 expectedRRectIntersection, rrect);
1680 ClipStack::ClipState
state = ClipStack::ClipState::kComplex;
1685 }
else if (opBits) {
1690 !expectedRectIntersection.
isEmpty());
1692 if (opBits == 0xf) {
1693 state = ClipStack::ClipState::kDeviceRect;
1696 SkASSERT(expectedRRectIntersection !=
1698 !expectedRRectIntersection.
isEmpty());
1700 if (opBits == 0xf) {
1701 state = ClipStack::ClipState::kDeviceRRect;
1719 ClipStack cs(kDeviceBounds,
nullptr,
false);
1724 SkIRect replace = {50, 25, 75, 40};
1726 cs.replaceClip(replace);
1728 REPORTER_ASSERT(r, cs.clipState() == ClipStack::ClipState::kDeviceRect,
1729 "Clip did not become a device rect");
1730 REPORTER_ASSERT(r, cs.getConservativeBounds() == replace,
"Unexpected replaced clip bounds");
1731 const ClipStack::Element& replaceElement = *cs.begin();
1736 "Unexpected replace element state");
1740 REPORTER_ASSERT(r, cs.clipState() == ClipStack::ClipState::kDeviceRRect,
1741 "Unexpected state after restore, not kDeviceRRect");
1742 const ClipStack::Element& rrectElem = *cs.begin();
1747 "RRect element state not restored properly after replace clip undone");
1764 ClipStack cs(kDeviceBounds, &
SkMatrix::I(),
false);
1767 for (
int y = 0;
y < 10; ++
y) {
1768 for (
int x = 0;
x < 10; ++
x) {
1789 ClipStack cs(kDeviceBounds,
nullptr,
true);
1792 SkRect aaRect = {0.25f, 12.43f, 25.2f, 23.f};
1796 SkPath nonAAPath = make_octagon({2.f, 10.f, 16.f, 20.f});
1800 SkRect nonAARect = {4.5f, 5.f, 17.25f, 18.23f};
1805 auto elements = cs.begin();
1807 const ClipStack::Element& nonAARectElement = *elements;
1808 REPORTER_ASSERT(r, nonAARectElement.fShape.isRect(),
"Expected rect element");
1810 "Axis-aligned non-AA rect ignores forceAA");
1812 "Mixed AA rects should not combine");
1815 const ClipStack::Element& aaPathElement = *elements;
1816 REPORTER_ASSERT(r, aaPathElement.fShape.isPath(),
"Expected path element");
1817 REPORTER_ASSERT(r, aaPathElement.fShape.path() == nonAAPath,
"Wrong path element");
1821 const ClipStack::Element& aaRectElement = *elements;
1822 REPORTER_ASSERT(r, aaRectElement.fShape.isRect(),
"Expected rect element");
1824 "Mixed AA rects should not combine");
1828 REPORTER_ASSERT(r, !(elements != cs.end()),
"Expected only three clip elements");
1836 ClipStack cs(kDeviceBounds,
nullptr,
false);
1841 "Offscreen draw is kClippedOut");
1846 "Wide open screen intersection is still kUnclipped");
1853 "Empty clip stack preApplies as kClippedOut");
1858 SkRect rect = {10.f, 10.f, 40.f, 40.f};
1864 "Draw contained within clip is kUnclipped");
1869 "Draw not intersecting clip is kClippedOut");
1874 SkPath path = make_octagon(rect.makeOutset(5.f, 5.f), 5.f, 5.f);
1878 "Draw with complex clip is kClipped, but is not an rrect");
1889 "kDeviceRect clip stack should be reported by preApply");
1900 result.fRRect == clipRRect,
1901 "kDeviceRRect clip stack should be reported by preApply");
1910 sk_sp<SkShader> shader = SkShaders::Color({0.f, 0.f, 0.f, 0.5f},
nullptr);
1918 ClipStack cs(kDeviceBounds, &
SkMatrix::I(),
false);
1920 cs.clipShader(shader);
1923 "A clip shader should be reported as a complex clip");
1926 SkRect drawBounds = {10.f, 11.f, 16.f, 32.f};
1931 "apply() should return kClipped for a clip shader");
1933 "apply() should have converted clip shader to a coverage FP");
1936 drawBounds = {-15.f, -10.f, -1.f, 10.f};
1940 "apply() should still discard offscreen draws with a clip shader");
1944 "restore() should get rid of the clip shader");
1950 SkASSERT(cs.clipState() == ClipStack::ClipState::kDeviceRect);
1951 cs.clipShader(shader);
1954 "A clip shader should not produce a device rect from preApply");
1971 ClipStack cs(kDeviceBounds, &
SkMatrix::I(),
false);
1975 SkRect drawBounds = {-15.f, -15.f, -1.f, -1.f};
1985 SkRect drawBounds = {15.4f, 16.3f, 26.f, 32.f};
1999 SkRect clipRect = {kDeviceBounds.fRight - 20.f, 10.f, kDeviceBounds.fRight, 20.f};
2000 SkRect drawRect = clipRect.makeOffset(10.f, 0.f);
2009 "Draw rect should be clipped to device rect");
2011 "After device clipping, this should be detected as contained within clip");
2017 SkRect clipRect = {15.3f, 17.23f, 30.2f, 50.8f};
2018 SkRect drawRect = clipRect.makeOutset(10.f, 10.f);
2019 SkIRect expectedScissor = clipRect.round();
2028 REPORTER_ASSERT(r, !out.hasCoverageFragmentProcessor(),
"Clip should not use coverage FPs");
2029 REPORTER_ASSERT(r, !out.hardClip().hasStencilClip(),
"Clip should not need stencil");
2031 "Clip should not need window rects");
2033 out.scissorState().rect() == expectedScissor,
2034 "Clip has unexpected scissor rectangle");
2039 auto testHasCoverageFP = [&](
SkRect drawBounds) {
2044 REPORTER_ASSERT(r, out.scissorState().enabled(),
"Coverage FPs should still set scissor");
2045 REPORTER_ASSERT(r, out.hasCoverageFragmentProcessor(),
"Clip should use coverage FP");
2053 testHasCoverageFP({9.f, 10.f, 30.f, 18.f});
2059 SkRect rect = {4.f, 8.f, 20.f, 20.f};
2063 testHasCoverageFP(rect.makeOffset(2.f, 2.f));
2069 SkRect rect = {14.f, 8.f, 30.f, 22.34f};
2073 testHasCoverageFP(rot.
mapRect(rect));
2079 SkRect rect = {15.f, 15.f, 45.f, 45.f};
2082 testHasCoverageFP(rect.makeOutset(2.f, 2.f));
2090 options->fAvoidStencilBuffers =
true;
2107 std::unique_ptr<ClipStack> cs(
new ClipStack(kDeviceBounds, &
SkMatrix::I(),
false));
2111 path.addCircle(
x,
y, radius);
2112 path.addCircle(
x + radius / 2.f,
y + radius / 2.f, radius);
2119 auto drawRect = [&](
SkRect drawBounds) {
2121 paint.setColor4f({1.f, 1.f, 1.f, 1.f});
2125 auto generateMask = [&](
SkRect drawBounds) {
2127 drawRect(drawBounds);
2129 REPORTER_ASSERT(r, priorKey != newKey,
"Did not generate a new SW mask key as expected");
2133 auto verifyKeys = [&](
const std::vector<skgpu::UniqueKey>& expectedKeys,
2134 const std::vector<skgpu::UniqueKey>& releasedKeys) {
2141 SkASSERT(expectedKeys.size() > 0 || releasedKeys.size() > 0);
2142 const char* tag = expectedKeys.size() > 0 ? expectedKeys[0].tag() : releasedKeys[0].tag();
2144 int numProxies = cache->countUniqueKeysWithTag(tag);
2146 "Unexpected proxy count, got %d, not %d",
2147 numProxies, (
int) expectedKeys.size());
2150 for (
const auto&
key : expectedKeys) {
2154 for (
const auto&
key : releasedKeys) {
2162 addMaskRequiringClip(5.f, 5.f, 20.f);
2165 verifyKeys({keyADepth1, keyBDepth1}, {});
2169 addMaskRequiringClip(6.f, 6.f, 15.f);
2172 verifyKeys({keyADepth1, keyBDepth1, keyADepth2, keyBDepth2}, {});
2175 addMaskRequiringClip(4.f, 4.f, 15.f);
2177 verifyKeys({keyADepth1, keyBDepth1, keyCDepth2}, {keyADepth2, keyBDepth2});
2181 verifyKeys({keyADepth1, keyBDepth1}, {keyCDepth2});
2184 drawRect({0.f, 0.f, 20.f, 20.f});
2185 drawRect({10.f, 10.f, 30.f, 30.f});
2186 verifyKeys({keyADepth1, keyBDepth1}, {});
2189 drawRect({5.f, 5.f, 15.f, 15.f});
2190 verifyKeys({keyADepth1, keyBDepth1}, {});
2194 verifyKeys({}, {keyADepth1, keyBDepth1});
static bool intersect(const SkPoint &p0, const SkPoint &n0, const SkPoint &p1, const SkPoint &n1, SkScalar *t)
static void disable_tessellation_atlas(GrContextOptions *options)
#define DEFINE_OP_CLASS_ID
static const uint16_t kTL
static const uint16_t kBL
static const uint16_t kBR
static const uint16_t kTR
#define SkAssertResult(cond)
#define SkDEBUGFAIL(message)
static bool contains(const SkRect &r, SkPoint p)
static size_t difference(size_t minuend, size_t subtrahend)
@ kLine_SkPathSegmentMask
void swap(sk_sp< T > &a, sk_sp< T > &b)
static constexpr bool SkToBool(const T &x)
#define DEF_TEST(name, reporter)
#define REPORTER_ASSERT(r, cond,...)
#define DEF_GANESH_TEST_FOR_CONTEXTS( name, context_filter, reporter, context_info, options_filter, ctsEnforcement)
Type::kYUV Type::kRGBA() int(0.7 *637)
static void run_test_case(const SkString &testdata, const SkBitmap &bitmap, SkCanvas *canvas)
static SkIRect GetPixelIBounds(const SkRect &bounds, GrAA aa, BoundsType mode=BoundsType::kExterior)
GrResourceCache * getResourceCache()
static sk_sp< GrDirectContext > MakeMock(const GrMockOptions *, const GrContextOptions &)
GrSemaphoresSubmitted flush(const GrFlushInfo &info)
GrDirectContextPriv priv()
static constexpr Analysis EmptySetAnalysis()
sk_sp< GrTextureProxy > findOrCreateProxyByUniqueKey(const skgpu::UniqueKey &, UseAllocator=UseAllocator::kYes)
GrProxyProvider * proxyProvider()
static sk_sp< SkColorSpace > MakeSRGB()
static SkMatrix Scale(SkScalar sx, SkScalar sy)
SkMatrix & postTranslate(SkScalar dx, SkScalar dy)
static SkMatrix RotateDeg(SkScalar deg)
SkMatrix & setRotate(SkScalar degrees, SkScalar px, SkScalar py)
static const SkMatrix & I()
bool isScaleTranslate() const
bool preservesAxisAlignment() const
bool mapRect(SkRect *dst, const SkRect &src, SkApplyPerspectiveClip pc=SkApplyPerspectiveClip::kYes) const
SkPath & moveTo(SkScalar x, SkScalar y)
SkPath & addRRect(const SkRRect &rrect, SkPathDirection dir=SkPathDirection::kCW)
void toggleInverseFillType()
SkPath & addOval(const SkRect &oval, SkPathDirection dir=SkPathDirection::kCW)
SkPath & addRect(const SkRect &rect, SkPathDirection dir, unsigned start)
static SkRect InnerBounds(const SkRRect &rr)
static SkRRect ConservativeIntersect(const SkRRect &a, const SkRRect &b)
static SkRRect MakeOval(const SkRect &oval)
bool transform(const SkMatrix &matrix, SkRRect *dst) const
static SkRRect MakeRect(const SkRect &r)
static SkRRect MakeRectXY(const SkRect &rect, SkScalar xRad, SkScalar yRad)
void setRectRadii(const SkRect &rect, const SkVector radii[4])
SkRRect makeOffset(SkScalar dx, SkScalar dy) const
@ kIntersect_Op
target intersected with operand
@ kDifference_Op
target minus operand
const SkIRect & getBounds() const
bool op(const SkIRect &rect, Op op)
const char * c_str() const
void void void appendf(const char format[],...) SK_PRINTF_LIKE(2
static std::unique_ptr< SurfaceDrawContext > Make(GrRecordingContext *, GrColorType, sk_sp< GrSurfaceProxy >, sk_sp< SkColorSpace >, GrSurfaceOrigin, const SkSurfaceProps &)
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
EMSCRIPTEN_KEEPALIVE void empty()
const GrXPFactory * Get(SkBlendMode mode)
ClipOpAndAA opAA SkRegion region
sk_sp< SkBlender > blender SkRect rect
Build(configs, env, options)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network policy
bool IsRenderingContext(skgpu::ContextType type)
int32_t fBottom
larger y-axis bounds
int32_t fTop
smaller y-axis bounds
static constexpr SkIRect MakeEmpty()
static constexpr SkIRect MakeWH(int32_t w, int32_t h)
int32_t fLeft
smaller x-axis bounds
bool contains(int32_t x, int32_t y) const
int32_t fRight
larger x-axis bounds
static SkRect Make(const SkISize &size)
static constexpr SkRect MakeEmpty()
SkScalar fBottom
larger y-axis bounds
void inset(float dx, float dy)
constexpr SkRect makeOffset(float dx, float dy) const
bool intersect(const SkRect &r)
SkScalar fLeft
smaller x-axis bounds
SkRect makeOutset(float dx, float dy) const
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
bool intersects(const SkRect &r) const
SkScalar fRight
larger x-axis bounds
void offset(float dx, float dy)
constexpr float height() const
constexpr float width() const
static constexpr SkRect MakeWH(float w, float h)
SkScalar fTop
smaller y-axis bounds