| epoger@google.com | ec3ed6a | 2011-07-28 14:26:00 +0000 | [diff] [blame] | 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 | */ | 
| tfarina@chromium.org | e4fafb1 | 2013-12-12 21:11:12 +0000 | [diff] [blame] | 7 |  | 
| reed@google.com | bdee9fc | 2011-02-22 20:17:43 +0000 | [diff] [blame] | 8 | #include "Test.h" | 
|  | 9 | #include "SkClipStack.h" | 
| vandebo@chromium.org | 1e1c36f | 2011-05-03 16:26:09 +0000 | [diff] [blame] | 10 | #include "SkPath.h" | 
| bsalomon@google.com | 51a6286 | 2012-11-26 21:19:43 +0000 | [diff] [blame] | 11 | #include "SkRandom.h" | 
| vandebo@chromium.org | 1e1c36f | 2011-05-03 16:26:09 +0000 | [diff] [blame] | 12 | #include "SkRect.h" | 
| bsalomon@google.com | 51a6286 | 2012-11-26 21:19:43 +0000 | [diff] [blame] | 13 | #include "SkRegion.h" | 
| robertphillips@google.com | 80214e2 | 2012-07-20 15:33:18 +0000 | [diff] [blame] | 14 |  | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 15 | #if SK_SUPPORT_GPU | 
|  | 16 | #include "GrReducedClip.h" | 
|  | 17 | typedef GrReducedClip::ElementList ElementList; | 
|  | 18 | typedef GrReducedClip::InitialState InitialState; | 
|  | 19 | #endif | 
|  | 20 |  | 
| vandebo@chromium.org | 1e1c36f | 2011-05-03 16:26:09 +0000 | [diff] [blame] | 21 | static void test_assign_and_comparison(skiatest::Reporter* reporter) { | 
|  | 22 | SkClipStack s; | 
| reed@google.com | d9f2dea | 2011-10-12 14:43:27 +0000 | [diff] [blame] | 23 | bool doAA = false; | 
| vandebo@chromium.org | 1e1c36f | 2011-05-03 16:26:09 +0000 | [diff] [blame] | 24 |  | 
| robertphillips@google.com | 80214e2 | 2012-07-20 15:33:18 +0000 | [diff] [blame] | 25 | REPORTER_ASSERT(reporter, 0 == s.getSaveCount()); | 
|  | 26 |  | 
| vandebo@chromium.org | 1e1c36f | 2011-05-03 16:26:09 +0000 | [diff] [blame] | 27 | // Build up a clip stack with a path, an empty clip, and a rect. | 
|  | 28 | s.save(); | 
| robertphillips@google.com | 80214e2 | 2012-07-20 15:33:18 +0000 | [diff] [blame] | 29 | REPORTER_ASSERT(reporter, 1 == s.getSaveCount()); | 
|  | 30 |  | 
| vandebo@chromium.org | 1e1c36f | 2011-05-03 16:26:09 +0000 | [diff] [blame] | 31 | SkPath p; | 
|  | 32 | p.moveTo(5, 6); | 
|  | 33 | p.lineTo(7, 8); | 
|  | 34 | p.lineTo(5, 9); | 
|  | 35 | p.close(); | 
| reed@google.com | d9f2dea | 2011-10-12 14:43:27 +0000 | [diff] [blame] | 36 | s.clipDevPath(p, SkRegion::kIntersect_Op, doAA); | 
| vandebo@chromium.org | 1e1c36f | 2011-05-03 16:26:09 +0000 | [diff] [blame] | 37 |  | 
|  | 38 | s.save(); | 
| robertphillips@google.com | 80214e2 | 2012-07-20 15:33:18 +0000 | [diff] [blame] | 39 | REPORTER_ASSERT(reporter, 2 == s.getSaveCount()); | 
|  | 40 |  | 
| vandebo@chromium.org | 1e1c36f | 2011-05-03 16:26:09 +0000 | [diff] [blame] | 41 | SkRect r = SkRect::MakeLTRB(1, 2, 3, 4); | 
| reed@google.com | d9f2dea | 2011-10-12 14:43:27 +0000 | [diff] [blame] | 42 | s.clipDevRect(r, SkRegion::kIntersect_Op, doAA); | 
| vandebo@chromium.org | 1e1c36f | 2011-05-03 16:26:09 +0000 | [diff] [blame] | 43 | r = SkRect::MakeLTRB(10, 11, 12, 13); | 
| reed@google.com | d9f2dea | 2011-10-12 14:43:27 +0000 | [diff] [blame] | 44 | s.clipDevRect(r, SkRegion::kIntersect_Op, doAA); | 
| vandebo@chromium.org | 1e1c36f | 2011-05-03 16:26:09 +0000 | [diff] [blame] | 45 |  | 
|  | 46 | s.save(); | 
| robertphillips@google.com | 80214e2 | 2012-07-20 15:33:18 +0000 | [diff] [blame] | 47 | REPORTER_ASSERT(reporter, 3 == s.getSaveCount()); | 
|  | 48 |  | 
| vandebo@chromium.org | 1e1c36f | 2011-05-03 16:26:09 +0000 | [diff] [blame] | 49 | r = SkRect::MakeLTRB(14, 15, 16, 17); | 
| reed@google.com | d9f2dea | 2011-10-12 14:43:27 +0000 | [diff] [blame] | 50 | s.clipDevRect(r, SkRegion::kUnion_Op, doAA); | 
| vandebo@chromium.org | 1e1c36f | 2011-05-03 16:26:09 +0000 | [diff] [blame] | 51 |  | 
|  | 52 | // Test that assignment works. | 
|  | 53 | SkClipStack copy = s; | 
|  | 54 | REPORTER_ASSERT(reporter, s == copy); | 
|  | 55 |  | 
|  | 56 | // Test that different save levels triggers not equal. | 
|  | 57 | s.restore(); | 
| robertphillips@google.com | 80214e2 | 2012-07-20 15:33:18 +0000 | [diff] [blame] | 58 | REPORTER_ASSERT(reporter, 2 == s.getSaveCount()); | 
| vandebo@chromium.org | 1e1c36f | 2011-05-03 16:26:09 +0000 | [diff] [blame] | 59 | REPORTER_ASSERT(reporter, s != copy); | 
|  | 60 |  | 
|  | 61 | // Test that an equal, but not copied version is equal. | 
|  | 62 | s.save(); | 
| robertphillips@google.com | 80214e2 | 2012-07-20 15:33:18 +0000 | [diff] [blame] | 63 | REPORTER_ASSERT(reporter, 3 == s.getSaveCount()); | 
| vandebo@chromium.org | 1e1c36f | 2011-05-03 16:26:09 +0000 | [diff] [blame] | 64 | r = SkRect::MakeLTRB(14, 15, 16, 17); | 
| reed@google.com | d9f2dea | 2011-10-12 14:43:27 +0000 | [diff] [blame] | 65 | s.clipDevRect(r, SkRegion::kUnion_Op, doAA); | 
| vandebo@chromium.org | 1e1c36f | 2011-05-03 16:26:09 +0000 | [diff] [blame] | 66 | REPORTER_ASSERT(reporter, s == copy); | 
|  | 67 |  | 
|  | 68 | // Test that a different op on one level triggers not equal. | 
|  | 69 | s.restore(); | 
| robertphillips@google.com | 80214e2 | 2012-07-20 15:33:18 +0000 | [diff] [blame] | 70 | REPORTER_ASSERT(reporter, 2 == s.getSaveCount()); | 
| vandebo@chromium.org | 1e1c36f | 2011-05-03 16:26:09 +0000 | [diff] [blame] | 71 | s.save(); | 
| robertphillips@google.com | 80214e2 | 2012-07-20 15:33:18 +0000 | [diff] [blame] | 72 | REPORTER_ASSERT(reporter, 3 == s.getSaveCount()); | 
| vandebo@chromium.org | 1e1c36f | 2011-05-03 16:26:09 +0000 | [diff] [blame] | 73 | r = SkRect::MakeLTRB(14, 15, 16, 17); | 
| reed@google.com | d9f2dea | 2011-10-12 14:43:27 +0000 | [diff] [blame] | 74 | s.clipDevRect(r, SkRegion::kIntersect_Op, doAA); | 
| vandebo@chromium.org | 1e1c36f | 2011-05-03 16:26:09 +0000 | [diff] [blame] | 75 | REPORTER_ASSERT(reporter, s != copy); | 
|  | 76 |  | 
| commit-bot@chromium.org | e5b2af9 | 2014-02-16 13:25:24 +0000 | [diff] [blame] | 77 | // Test that version constructed with rect-path rather than a rect is still considered equal. | 
| vandebo@chromium.org | 1e1c36f | 2011-05-03 16:26:09 +0000 | [diff] [blame] | 78 | s.restore(); | 
|  | 79 | s.save(); | 
|  | 80 | SkPath rp; | 
|  | 81 | rp.addRect(r); | 
| reed@google.com | d9f2dea | 2011-10-12 14:43:27 +0000 | [diff] [blame] | 82 | s.clipDevPath(rp, SkRegion::kUnion_Op, doAA); | 
| commit-bot@chromium.org | e5b2af9 | 2014-02-16 13:25:24 +0000 | [diff] [blame] | 83 | REPORTER_ASSERT(reporter, s == copy); | 
| vandebo@chromium.org | 1e1c36f | 2011-05-03 16:26:09 +0000 | [diff] [blame] | 84 |  | 
|  | 85 | // Test that different rects triggers not equal. | 
|  | 86 | s.restore(); | 
| robertphillips@google.com | 80214e2 | 2012-07-20 15:33:18 +0000 | [diff] [blame] | 87 | REPORTER_ASSERT(reporter, 2 == s.getSaveCount()); | 
| vandebo@chromium.org | 1e1c36f | 2011-05-03 16:26:09 +0000 | [diff] [blame] | 88 | s.save(); | 
| robertphillips@google.com | 80214e2 | 2012-07-20 15:33:18 +0000 | [diff] [blame] | 89 | REPORTER_ASSERT(reporter, 3 == s.getSaveCount()); | 
|  | 90 |  | 
| vandebo@chromium.org | 1e1c36f | 2011-05-03 16:26:09 +0000 | [diff] [blame] | 91 | r = SkRect::MakeLTRB(24, 25, 26, 27); | 
| reed@google.com | d9f2dea | 2011-10-12 14:43:27 +0000 | [diff] [blame] | 92 | s.clipDevRect(r, SkRegion::kUnion_Op, doAA); | 
| vandebo@chromium.org | 1e1c36f | 2011-05-03 16:26:09 +0000 | [diff] [blame] | 93 | REPORTER_ASSERT(reporter, s != copy); | 
|  | 94 |  | 
|  | 95 | // Sanity check | 
|  | 96 | s.restore(); | 
| robertphillips@google.com | 80214e2 | 2012-07-20 15:33:18 +0000 | [diff] [blame] | 97 | REPORTER_ASSERT(reporter, 2 == s.getSaveCount()); | 
|  | 98 |  | 
| vandebo@chromium.org | 1e1c36f | 2011-05-03 16:26:09 +0000 | [diff] [blame] | 99 | copy.restore(); | 
| robertphillips@google.com | 80214e2 | 2012-07-20 15:33:18 +0000 | [diff] [blame] | 100 | REPORTER_ASSERT(reporter, 2 == copy.getSaveCount()); | 
| vandebo@chromium.org | 1e1c36f | 2011-05-03 16:26:09 +0000 | [diff] [blame] | 101 | REPORTER_ASSERT(reporter, s == copy); | 
|  | 102 | s.restore(); | 
| robertphillips@google.com | 80214e2 | 2012-07-20 15:33:18 +0000 | [diff] [blame] | 103 | REPORTER_ASSERT(reporter, 1 == s.getSaveCount()); | 
| vandebo@chromium.org | 1e1c36f | 2011-05-03 16:26:09 +0000 | [diff] [blame] | 104 | copy.restore(); | 
| robertphillips@google.com | 80214e2 | 2012-07-20 15:33:18 +0000 | [diff] [blame] | 105 | REPORTER_ASSERT(reporter, 1 == copy.getSaveCount()); | 
| vandebo@chromium.org | 1e1c36f | 2011-05-03 16:26:09 +0000 | [diff] [blame] | 106 | REPORTER_ASSERT(reporter, s == copy); | 
|  | 107 |  | 
|  | 108 | // Test that different paths triggers not equal. | 
|  | 109 | s.restore(); | 
| robertphillips@google.com | 80214e2 | 2012-07-20 15:33:18 +0000 | [diff] [blame] | 110 | REPORTER_ASSERT(reporter, 0 == s.getSaveCount()); | 
| vandebo@chromium.org | 1e1c36f | 2011-05-03 16:26:09 +0000 | [diff] [blame] | 111 | s.save(); | 
| robertphillips@google.com | 80214e2 | 2012-07-20 15:33:18 +0000 | [diff] [blame] | 112 | REPORTER_ASSERT(reporter, 1 == s.getSaveCount()); | 
|  | 113 |  | 
| vandebo@chromium.org | 1e1c36f | 2011-05-03 16:26:09 +0000 | [diff] [blame] | 114 | p.addRect(r); | 
| reed@google.com | d9f2dea | 2011-10-12 14:43:27 +0000 | [diff] [blame] | 115 | s.clipDevPath(p, SkRegion::kIntersect_Op, doAA); | 
| vandebo@chromium.org | 1e1c36f | 2011-05-03 16:26:09 +0000 | [diff] [blame] | 116 | REPORTER_ASSERT(reporter, s != copy); | 
|  | 117 | } | 
| reed@google.com | bdee9fc | 2011-02-22 20:17:43 +0000 | [diff] [blame] | 118 |  | 
|  | 119 | static void assert_count(skiatest::Reporter* reporter, const SkClipStack& stack, | 
|  | 120 | int count) { | 
| robertphillips@google.com | 80214e2 | 2012-07-20 15:33:18 +0000 | [diff] [blame] | 121 | SkClipStack::B2TIter iter(stack); | 
| reed@google.com | bdee9fc | 2011-02-22 20:17:43 +0000 | [diff] [blame] | 122 | int counter = 0; | 
|  | 123 | while (iter.next()) { | 
|  | 124 | counter += 1; | 
|  | 125 | } | 
|  | 126 | REPORTER_ASSERT(reporter, count == counter); | 
|  | 127 | } | 
|  | 128 |  | 
| robertphillips@google.com | 08eacc1 | 2012-08-02 12:49:00 +0000 | [diff] [blame] | 129 | // Exercise the SkClipStack's bottom to top and bidirectional iterators | 
|  | 130 | // (including the skipToTopmost functionality) | 
| robertphillips@google.com | 80214e2 | 2012-07-20 15:33:18 +0000 | [diff] [blame] | 131 | static void test_iterators(skiatest::Reporter* reporter) { | 
|  | 132 | SkClipStack stack; | 
|  | 133 |  | 
|  | 134 | static const SkRect gRects[] = { | 
|  | 135 | { 0,   0,  40,  40 }, | 
|  | 136 | { 60,  0, 100,  40 }, | 
|  | 137 | { 0,  60,  40, 100 }, | 
|  | 138 | { 60, 60, 100, 100 } | 
|  | 139 | }; | 
|  | 140 |  | 
|  | 141 | for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) { | 
|  | 142 | // the union op will prevent these from being fused together | 
|  | 143 | stack.clipDevRect(gRects[i], SkRegion::kUnion_Op, false); | 
|  | 144 | } | 
|  | 145 |  | 
|  | 146 | assert_count(reporter, stack, 4); | 
|  | 147 |  | 
|  | 148 | // bottom to top iteration | 
|  | 149 | { | 
| halcanary | 96fcdcc | 2015-08-27 07:41:13 -0700 | [diff] [blame] | 150 | const SkClipStack::Element* element = nullptr; | 
| robertphillips@google.com | 80214e2 | 2012-07-20 15:33:18 +0000 | [diff] [blame] | 151 |  | 
|  | 152 | SkClipStack::B2TIter iter(stack); | 
|  | 153 | int i; | 
|  | 154 |  | 
| bsalomon@google.com | 8182fa0 | 2012-12-04 14:06:06 +0000 | [diff] [blame] | 155 | for (i = 0, element = iter.next(); element; ++i, element = iter.next()) { | 
|  | 156 | REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType()); | 
|  | 157 | REPORTER_ASSERT(reporter, element->getRect() == gRects[i]); | 
| robertphillips@google.com | 80214e2 | 2012-07-20 15:33:18 +0000 | [diff] [blame] | 158 | } | 
|  | 159 |  | 
|  | 160 | SkASSERT(i == 4); | 
|  | 161 | } | 
|  | 162 |  | 
|  | 163 | // top to bottom iteration | 
|  | 164 | { | 
| halcanary | 96fcdcc | 2015-08-27 07:41:13 -0700 | [diff] [blame] | 165 | const SkClipStack::Element* element = nullptr; | 
| robertphillips@google.com | 80214e2 | 2012-07-20 15:33:18 +0000 | [diff] [blame] | 166 |  | 
|  | 167 | SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart); | 
|  | 168 | int i; | 
|  | 169 |  | 
| bsalomon@google.com | 8182fa0 | 2012-12-04 14:06:06 +0000 | [diff] [blame] | 170 | for (i = 3, element = iter.prev(); element; --i, element = iter.prev()) { | 
|  | 171 | REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType()); | 
|  | 172 | REPORTER_ASSERT(reporter, element->getRect() == gRects[i]); | 
| robertphillips@google.com | 80214e2 | 2012-07-20 15:33:18 +0000 | [diff] [blame] | 173 | } | 
|  | 174 |  | 
|  | 175 | SkASSERT(i == -1); | 
|  | 176 | } | 
|  | 177 |  | 
|  | 178 | // skipToTopmost | 
|  | 179 | { | 
| halcanary | 96fcdcc | 2015-08-27 07:41:13 -0700 | [diff] [blame] | 180 | const SkClipStack::Element* element = nullptr; | 
| robertphillips@google.com | 80214e2 | 2012-07-20 15:33:18 +0000 | [diff] [blame] | 181 |  | 
|  | 182 | SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart); | 
|  | 183 |  | 
| bsalomon@google.com | 8182fa0 | 2012-12-04 14:06:06 +0000 | [diff] [blame] | 184 | element = iter.skipToTopmost(SkRegion::kUnion_Op); | 
|  | 185 | REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType()); | 
|  | 186 | REPORTER_ASSERT(reporter, element->getRect() == gRects[3]); | 
| robertphillips@google.com | 80214e2 | 2012-07-20 15:33:18 +0000 | [diff] [blame] | 187 | } | 
|  | 188 | } | 
|  | 189 |  | 
| robertphillips@google.com | 08eacc1 | 2012-08-02 12:49:00 +0000 | [diff] [blame] | 190 | // Exercise the SkClipStack's getConservativeBounds computation | 
| commit-bot@chromium.org | e5b2af9 | 2014-02-16 13:25:24 +0000 | [diff] [blame] | 191 | static void test_bounds(skiatest::Reporter* reporter, SkClipStack::Element::Type primType) { | 
| robertphillips@google.com | 607fe07 | 2012-07-24 13:54:00 +0000 | [diff] [blame] | 192 | static const int gNumCases = 20; | 
|  | 193 | static const SkRect gAnswerRectsBW[gNumCases] = { | 
|  | 194 | // A op B | 
|  | 195 | { 40, 40, 50, 50 }, | 
|  | 196 | { 10, 10, 50, 50 }, | 
|  | 197 | { 10, 10, 80, 80 }, | 
|  | 198 | { 10, 10, 80, 80 }, | 
|  | 199 | { 40, 40, 80, 80 }, | 
|  | 200 |  | 
|  | 201 | // invA op B | 
|  | 202 | { 40, 40, 80, 80 }, | 
|  | 203 | { 0, 0, 100, 100 }, | 
|  | 204 | { 0, 0, 100, 100 }, | 
|  | 205 | { 0, 0, 100, 100 }, | 
|  | 206 | { 40, 40, 50, 50 }, | 
|  | 207 |  | 
|  | 208 | // A op invB | 
|  | 209 | { 10, 10, 50, 50 }, | 
|  | 210 | { 40, 40, 50, 50 }, | 
|  | 211 | { 0, 0, 100, 100 }, | 
|  | 212 | { 0, 0, 100, 100 }, | 
|  | 213 | { 0, 0, 100, 100 }, | 
|  | 214 |  | 
|  | 215 | // invA op invB | 
|  | 216 | { 0, 0, 100, 100 }, | 
|  | 217 | { 40, 40, 80, 80 }, | 
|  | 218 | { 0, 0, 100, 100 }, | 
|  | 219 | { 10, 10, 80, 80 }, | 
|  | 220 | { 10, 10, 50, 50 }, | 
|  | 221 | }; | 
|  | 222 |  | 
|  | 223 | static const SkRegion::Op gOps[] = { | 
|  | 224 | SkRegion::kIntersect_Op, | 
|  | 225 | SkRegion::kDifference_Op, | 
|  | 226 | SkRegion::kUnion_Op, | 
|  | 227 | SkRegion::kXOR_Op, | 
|  | 228 | SkRegion::kReverseDifference_Op | 
|  | 229 | }; | 
|  | 230 |  | 
|  | 231 | SkRect rectA, rectB; | 
|  | 232 |  | 
|  | 233 | rectA.iset(10, 10, 50, 50); | 
|  | 234 | rectB.iset(40, 40, 80, 80); | 
|  | 235 |  | 
| commit-bot@chromium.org | e5b2af9 | 2014-02-16 13:25:24 +0000 | [diff] [blame] | 236 | SkRRect rrectA, rrectB; | 
|  | 237 | rrectA.setOval(rectA); | 
|  | 238 | rrectB.setRectXY(rectB, SkIntToScalar(1), SkIntToScalar(2)); | 
| robertphillips@google.com | 607fe07 | 2012-07-24 13:54:00 +0000 | [diff] [blame] | 239 |  | 
| commit-bot@chromium.org | e5b2af9 | 2014-02-16 13:25:24 +0000 | [diff] [blame] | 240 | SkPath pathA, pathB; | 
|  | 241 |  | 
|  | 242 | pathA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5)); | 
|  | 243 | pathB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5)); | 
| robertphillips@google.com | 607fe07 | 2012-07-24 13:54:00 +0000 | [diff] [blame] | 244 |  | 
|  | 245 | SkClipStack stack; | 
| robertphillips@google.com | 7b11289 | 2012-07-31 15:18:21 +0000 | [diff] [blame] | 246 | SkRect devClipBound; | 
| robertphillips@google.com | 4c2a2f7 | 2012-07-24 22:07:50 +0000 | [diff] [blame] | 247 | bool isIntersectionOfRects = false; | 
| robertphillips@google.com | 607fe07 | 2012-07-24 13:54:00 +0000 | [diff] [blame] | 248 |  | 
|  | 249 | int testCase = 0; | 
| commit-bot@chromium.org | e5b2af9 | 2014-02-16 13:25:24 +0000 | [diff] [blame] | 250 | int numBitTests = SkClipStack::Element::kPath_Type == primType ? 4 : 1; | 
| robertphillips@google.com | 4c2a2f7 | 2012-07-24 22:07:50 +0000 | [diff] [blame] | 251 | for (int invBits = 0; invBits < numBitTests; ++invBits) { | 
| robertphillips@google.com | 607fe07 | 2012-07-24 13:54:00 +0000 | [diff] [blame] | 252 | for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); ++op) { | 
|  | 253 |  | 
|  | 254 | stack.save(); | 
|  | 255 | bool doInvA = SkToBool(invBits & 1); | 
|  | 256 | bool doInvB = SkToBool(invBits & 2); | 
|  | 257 |  | 
| commit-bot@chromium.org | e5b2af9 | 2014-02-16 13:25:24 +0000 | [diff] [blame] | 258 | pathA.setFillType(doInvA ? SkPath::kInverseEvenOdd_FillType : | 
| robertphillips@google.com | 607fe07 | 2012-07-24 13:54:00 +0000 | [diff] [blame] | 259 | SkPath::kEvenOdd_FillType); | 
| commit-bot@chromium.org | e5b2af9 | 2014-02-16 13:25:24 +0000 | [diff] [blame] | 260 | pathB.setFillType(doInvB ? SkPath::kInverseEvenOdd_FillType : | 
| robertphillips@google.com | 607fe07 | 2012-07-24 13:54:00 +0000 | [diff] [blame] | 261 | SkPath::kEvenOdd_FillType); | 
|  | 262 |  | 
| commit-bot@chromium.org | e5b2af9 | 2014-02-16 13:25:24 +0000 | [diff] [blame] | 263 | switch (primType) { | 
|  | 264 | case SkClipStack::Element::kEmpty_Type: | 
|  | 265 | SkDEBUGFAIL("Don't call this with kEmpty."); | 
|  | 266 | break; | 
|  | 267 | case SkClipStack::Element::kRect_Type: | 
|  | 268 | stack.clipDevRect(rectA, SkRegion::kIntersect_Op, false); | 
|  | 269 | stack.clipDevRect(rectB, gOps[op], false); | 
|  | 270 | break; | 
|  | 271 | case SkClipStack::Element::kRRect_Type: | 
|  | 272 | stack.clipDevRRect(rrectA, SkRegion::kIntersect_Op, false); | 
|  | 273 | stack.clipDevRRect(rrectB, gOps[op], false); | 
|  | 274 | break; | 
|  | 275 | case SkClipStack::Element::kPath_Type: | 
|  | 276 | stack.clipDevPath(pathA, SkRegion::kIntersect_Op, false); | 
|  | 277 | stack.clipDevPath(pathB, gOps[op], false); | 
|  | 278 | break; | 
| robertphillips@google.com | 4c2a2f7 | 2012-07-24 22:07:50 +0000 | [diff] [blame] | 279 | } | 
| robertphillips@google.com | 607fe07 | 2012-07-24 13:54:00 +0000 | [diff] [blame] | 280 |  | 
| robertphillips@google.com | cc6493b | 2012-07-26 18:39:13 +0000 | [diff] [blame] | 281 | REPORTER_ASSERT(reporter, !stack.isWideOpen()); | 
| commit-bot@chromium.org | d3e5842 | 2013-11-05 15:03:08 +0000 | [diff] [blame] | 282 | REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID()); | 
| robertphillips@google.com | cc6493b | 2012-07-26 18:39:13 +0000 | [diff] [blame] | 283 |  | 
| robertphillips@google.com | 7b11289 | 2012-07-31 15:18:21 +0000 | [diff] [blame] | 284 | stack.getConservativeBounds(0, 0, 100, 100, &devClipBound, | 
| robertphillips@google.com | 4c2a2f7 | 2012-07-24 22:07:50 +0000 | [diff] [blame] | 285 | &isIntersectionOfRects); | 
|  | 286 |  | 
| commit-bot@chromium.org | e5b2af9 | 2014-02-16 13:25:24 +0000 | [diff] [blame] | 287 | if (SkClipStack::Element::kRect_Type == primType) { | 
| rmistry@google.com | d6176b0 | 2012-08-23 18:14:13 +0000 | [diff] [blame] | 288 | REPORTER_ASSERT(reporter, isIntersectionOfRects == | 
| robertphillips@google.com | 4c2a2f7 | 2012-07-24 22:07:50 +0000 | [diff] [blame] | 289 | (gOps[op] == SkRegion::kIntersect_Op)); | 
|  | 290 | } else { | 
|  | 291 | REPORTER_ASSERT(reporter, !isIntersectionOfRects); | 
|  | 292 | } | 
| robertphillips@google.com | 607fe07 | 2012-07-24 13:54:00 +0000 | [diff] [blame] | 293 |  | 
|  | 294 | SkASSERT(testCase < gNumCases); | 
| robertphillips@google.com | 7b11289 | 2012-07-31 15:18:21 +0000 | [diff] [blame] | 295 | REPORTER_ASSERT(reporter, devClipBound == gAnswerRectsBW[testCase]); | 
| robertphillips@google.com | 607fe07 | 2012-07-24 13:54:00 +0000 | [diff] [blame] | 296 | ++testCase; | 
|  | 297 |  | 
|  | 298 | stack.restore(); | 
|  | 299 | } | 
|  | 300 | } | 
|  | 301 | } | 
|  | 302 |  | 
| robertphillips@google.com | cc6493b | 2012-07-26 18:39:13 +0000 | [diff] [blame] | 303 | // Test out 'isWideOpen' entry point | 
|  | 304 | static void test_isWideOpen(skiatest::Reporter* reporter) { | 
| commit-bot@chromium.org | d3e5842 | 2013-11-05 15:03:08 +0000 | [diff] [blame] | 305 | { | 
|  | 306 | // Empty stack is wide open. Wide open stack means that gen id is wide open. | 
|  | 307 | SkClipStack stack; | 
|  | 308 | REPORTER_ASSERT(reporter, stack.isWideOpen()); | 
|  | 309 | REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID()); | 
|  | 310 | } | 
| robertphillips@google.com | cc6493b | 2012-07-26 18:39:13 +0000 | [diff] [blame] | 311 |  | 
|  | 312 | SkRect rectA, rectB; | 
|  | 313 |  | 
|  | 314 | rectA.iset(10, 10, 40, 40); | 
|  | 315 | rectB.iset(50, 50, 80, 80); | 
|  | 316 |  | 
|  | 317 | // Stack should initially be wide open | 
|  | 318 | { | 
|  | 319 | SkClipStack stack; | 
|  | 320 |  | 
|  | 321 | REPORTER_ASSERT(reporter, stack.isWideOpen()); | 
| commit-bot@chromium.org | d3e5842 | 2013-11-05 15:03:08 +0000 | [diff] [blame] | 322 | REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID()); | 
| robertphillips@google.com | cc6493b | 2012-07-26 18:39:13 +0000 | [diff] [blame] | 323 | } | 
|  | 324 |  | 
|  | 325 | // Test out case where the user specifies a union that includes everything | 
|  | 326 | { | 
|  | 327 | SkClipStack stack; | 
|  | 328 |  | 
|  | 329 | SkPath clipA, clipB; | 
|  | 330 |  | 
|  | 331 | clipA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5)); | 
|  | 332 | clipA.setFillType(SkPath::kInverseEvenOdd_FillType); | 
|  | 333 |  | 
|  | 334 | clipB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5)); | 
|  | 335 | clipB.setFillType(SkPath::kInverseEvenOdd_FillType); | 
|  | 336 |  | 
|  | 337 | stack.clipDevPath(clipA, SkRegion::kReplace_Op, false); | 
|  | 338 | stack.clipDevPath(clipB, SkRegion::kUnion_Op, false); | 
|  | 339 |  | 
|  | 340 | REPORTER_ASSERT(reporter, stack.isWideOpen()); | 
| commit-bot@chromium.org | d3e5842 | 2013-11-05 15:03:08 +0000 | [diff] [blame] | 341 | REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID()); | 
| robertphillips@google.com | cc6493b | 2012-07-26 18:39:13 +0000 | [diff] [blame] | 342 | } | 
|  | 343 |  | 
|  | 344 | // Test out union w/ a wide open clip | 
|  | 345 | { | 
|  | 346 | SkClipStack stack; | 
|  | 347 |  | 
|  | 348 | stack.clipDevRect(rectA, SkRegion::kUnion_Op, false); | 
|  | 349 |  | 
|  | 350 | REPORTER_ASSERT(reporter, stack.isWideOpen()); | 
| commit-bot@chromium.org | d3e5842 | 2013-11-05 15:03:08 +0000 | [diff] [blame] | 351 | REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID()); | 
| robertphillips@google.com | cc6493b | 2012-07-26 18:39:13 +0000 | [diff] [blame] | 352 | } | 
|  | 353 |  | 
|  | 354 | // Test out empty difference from a wide open clip | 
|  | 355 | { | 
|  | 356 | SkClipStack stack; | 
|  | 357 |  | 
|  | 358 | SkRect emptyRect; | 
|  | 359 | emptyRect.setEmpty(); | 
|  | 360 |  | 
|  | 361 | stack.clipDevRect(emptyRect, SkRegion::kDifference_Op, false); | 
|  | 362 |  | 
|  | 363 | REPORTER_ASSERT(reporter, stack.isWideOpen()); | 
| commit-bot@chromium.org | d3e5842 | 2013-11-05 15:03:08 +0000 | [diff] [blame] | 364 | REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID()); | 
| robertphillips@google.com | cc6493b | 2012-07-26 18:39:13 +0000 | [diff] [blame] | 365 | } | 
|  | 366 |  | 
|  | 367 | // Test out return to wide open | 
|  | 368 | { | 
|  | 369 | SkClipStack stack; | 
|  | 370 |  | 
|  | 371 | stack.save(); | 
|  | 372 |  | 
|  | 373 | stack.clipDevRect(rectA, SkRegion::kReplace_Op, false); | 
|  | 374 |  | 
|  | 375 | REPORTER_ASSERT(reporter, !stack.isWideOpen()); | 
| commit-bot@chromium.org | d3e5842 | 2013-11-05 15:03:08 +0000 | [diff] [blame] | 376 | REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID()); | 
| robertphillips@google.com | cc6493b | 2012-07-26 18:39:13 +0000 | [diff] [blame] | 377 |  | 
|  | 378 | stack.restore(); | 
|  | 379 |  | 
|  | 380 | REPORTER_ASSERT(reporter, stack.isWideOpen()); | 
| commit-bot@chromium.org | d3e5842 | 2013-11-05 15:03:08 +0000 | [diff] [blame] | 381 | REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID()); | 
| robertphillips@google.com | cc6493b | 2012-07-26 18:39:13 +0000 | [diff] [blame] | 382 | } | 
|  | 383 | } | 
|  | 384 |  | 
| bsalomon@google.com | 100abf4 | 2012-09-05 17:40:04 +0000 | [diff] [blame] | 385 | static int count(const SkClipStack& stack) { | 
| robertphillips@google.com | 08eacc1 | 2012-08-02 12:49:00 +0000 | [diff] [blame] | 386 |  | 
|  | 387 | SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart); | 
|  | 388 |  | 
| halcanary | 96fcdcc | 2015-08-27 07:41:13 -0700 | [diff] [blame] | 389 | const SkClipStack::Element* element = nullptr; | 
| robertphillips@google.com | 08eacc1 | 2012-08-02 12:49:00 +0000 | [diff] [blame] | 390 | int count = 0; | 
|  | 391 |  | 
| bsalomon@google.com | 8182fa0 | 2012-12-04 14:06:06 +0000 | [diff] [blame] | 392 | for (element = iter.prev(); element; element = iter.prev(), ++count) { | 
| robertphillips@google.com | 08eacc1 | 2012-08-02 12:49:00 +0000 | [diff] [blame] | 393 | ; | 
|  | 394 | } | 
|  | 395 |  | 
|  | 396 | return count; | 
|  | 397 | } | 
|  | 398 |  | 
| junov@chromium.org | edf32d5 | 2012-12-10 14:57:54 +0000 | [diff] [blame] | 399 | static void test_rect_inverse_fill(skiatest::Reporter* reporter) { | 
|  | 400 | // non-intersecting rectangles | 
|  | 401 | SkRect rect  = SkRect::MakeLTRB(0, 0, 10, 10); | 
|  | 402 |  | 
|  | 403 | SkPath path; | 
|  | 404 | path.addRect(rect); | 
|  | 405 | path.toggleInverseFillType(); | 
|  | 406 | SkClipStack stack; | 
|  | 407 | stack.clipDevPath(path, SkRegion::kIntersect_Op, false); | 
|  | 408 |  | 
|  | 409 | SkRect bounds; | 
|  | 410 | SkClipStack::BoundsType boundsType; | 
|  | 411 | stack.getBounds(&bounds, &boundsType); | 
|  | 412 | REPORTER_ASSERT(reporter, SkClipStack::kInsideOut_BoundsType == boundsType); | 
|  | 413 | REPORTER_ASSERT(reporter, bounds == rect); | 
|  | 414 | } | 
|  | 415 |  | 
| commit-bot@chromium.org | 6fbe54c | 2013-06-11 11:01:48 +0000 | [diff] [blame] | 416 | static void test_rect_replace(skiatest::Reporter* reporter) { | 
|  | 417 | SkRect rect = SkRect::MakeWH(100, 100); | 
|  | 418 | SkRect rect2 = SkRect::MakeXYWH(50, 50, 100, 100); | 
|  | 419 |  | 
|  | 420 | SkRect bound; | 
|  | 421 | SkClipStack::BoundsType type; | 
|  | 422 | bool isIntersectionOfRects; | 
|  | 423 |  | 
|  | 424 | // Adding a new rect with the replace operator should not increase | 
|  | 425 | // the stack depth. BW replacing BW. | 
|  | 426 | { | 
|  | 427 | SkClipStack stack; | 
|  | 428 | REPORTER_ASSERT(reporter, 0 == count(stack)); | 
|  | 429 | stack.clipDevRect(rect, SkRegion::kReplace_Op, false); | 
|  | 430 | REPORTER_ASSERT(reporter, 1 == count(stack)); | 
|  | 431 | stack.clipDevRect(rect, SkRegion::kReplace_Op, false); | 
|  | 432 | REPORTER_ASSERT(reporter, 1 == count(stack)); | 
|  | 433 | } | 
|  | 434 |  | 
|  | 435 | // Adding a new rect with the replace operator should not increase | 
|  | 436 | // the stack depth. AA replacing AA. | 
|  | 437 | { | 
|  | 438 | SkClipStack stack; | 
|  | 439 | REPORTER_ASSERT(reporter, 0 == count(stack)); | 
|  | 440 | stack.clipDevRect(rect, SkRegion::kReplace_Op, true); | 
|  | 441 | REPORTER_ASSERT(reporter, 1 == count(stack)); | 
|  | 442 | stack.clipDevRect(rect, SkRegion::kReplace_Op, true); | 
|  | 443 | REPORTER_ASSERT(reporter, 1 == count(stack)); | 
|  | 444 | } | 
|  | 445 |  | 
|  | 446 | // Adding a new rect with the replace operator should not increase | 
|  | 447 | // the stack depth. BW replacing AA replacing BW. | 
|  | 448 | { | 
|  | 449 | SkClipStack stack; | 
|  | 450 | REPORTER_ASSERT(reporter, 0 == count(stack)); | 
|  | 451 | stack.clipDevRect(rect, SkRegion::kReplace_Op, false); | 
|  | 452 | REPORTER_ASSERT(reporter, 1 == count(stack)); | 
|  | 453 | stack.clipDevRect(rect, SkRegion::kReplace_Op, true); | 
|  | 454 | REPORTER_ASSERT(reporter, 1 == count(stack)); | 
|  | 455 | stack.clipDevRect(rect, SkRegion::kReplace_Op, false); | 
|  | 456 | REPORTER_ASSERT(reporter, 1 == count(stack)); | 
|  | 457 | } | 
|  | 458 |  | 
|  | 459 | // Make sure replace clip rects don't collapse too much. | 
|  | 460 | { | 
|  | 461 | SkClipStack stack; | 
|  | 462 | stack.clipDevRect(rect, SkRegion::kReplace_Op, false); | 
|  | 463 | stack.clipDevRect(rect2, SkRegion::kIntersect_Op, false); | 
|  | 464 | REPORTER_ASSERT(reporter, 1 == count(stack)); | 
|  | 465 |  | 
|  | 466 | stack.save(); | 
|  | 467 | stack.clipDevRect(rect, SkRegion::kReplace_Op, false); | 
|  | 468 | REPORTER_ASSERT(reporter, 2 == count(stack)); | 
|  | 469 | stack.getBounds(&bound, &type, &isIntersectionOfRects); | 
|  | 470 | REPORTER_ASSERT(reporter, bound == rect); | 
|  | 471 | stack.restore(); | 
|  | 472 | REPORTER_ASSERT(reporter, 1 == count(stack)); | 
|  | 473 |  | 
|  | 474 | stack.save(); | 
|  | 475 | stack.clipDevRect(rect, SkRegion::kReplace_Op, false); | 
|  | 476 | stack.clipDevRect(rect, SkRegion::kReplace_Op, false); | 
|  | 477 | REPORTER_ASSERT(reporter, 2 == count(stack)); | 
|  | 478 | stack.restore(); | 
|  | 479 | REPORTER_ASSERT(reporter, 1 == count(stack)); | 
|  | 480 |  | 
|  | 481 | stack.save(); | 
|  | 482 | stack.clipDevRect(rect, SkRegion::kReplace_Op, false); | 
|  | 483 | stack.clipDevRect(rect2, SkRegion::kIntersect_Op, false); | 
|  | 484 | stack.clipDevRect(rect, SkRegion::kReplace_Op, false); | 
|  | 485 | REPORTER_ASSERT(reporter, 2 == count(stack)); | 
|  | 486 | stack.restore(); | 
|  | 487 | REPORTER_ASSERT(reporter, 1 == count(stack)); | 
|  | 488 | } | 
|  | 489 | } | 
|  | 490 |  | 
|  | 491 | // Simplified path-based version of test_rect_replace. | 
|  | 492 | static void test_path_replace(skiatest::Reporter* reporter) { | 
|  | 493 | SkRect rect = SkRect::MakeWH(100, 100); | 
|  | 494 | SkPath path; | 
|  | 495 | path.addCircle(50, 50, 50); | 
|  | 496 |  | 
|  | 497 | // Replace operation doesn't grow the stack. | 
|  | 498 | { | 
|  | 499 | SkClipStack stack; | 
|  | 500 | REPORTER_ASSERT(reporter, 0 == count(stack)); | 
|  | 501 | stack.clipDevPath(path, SkRegion::kReplace_Op, false); | 
|  | 502 | REPORTER_ASSERT(reporter, 1 == count(stack)); | 
|  | 503 | stack.clipDevPath(path, SkRegion::kReplace_Op, false); | 
|  | 504 | REPORTER_ASSERT(reporter, 1 == count(stack)); | 
|  | 505 | } | 
|  | 506 |  | 
|  | 507 | // Replacing rect with path. | 
|  | 508 | { | 
|  | 509 | SkClipStack stack; | 
|  | 510 | stack.clipDevRect(rect, SkRegion::kReplace_Op, true); | 
|  | 511 | REPORTER_ASSERT(reporter, 1 == count(stack)); | 
|  | 512 | stack.clipDevPath(path, SkRegion::kReplace_Op, true); | 
|  | 513 | REPORTER_ASSERT(reporter, 1 == count(stack)); | 
|  | 514 | } | 
|  | 515 | } | 
|  | 516 |  | 
| robertphillips@google.com | 08eacc1 | 2012-08-02 12:49:00 +0000 | [diff] [blame] | 517 | // Test out SkClipStack's merging of rect clips. In particular exercise | 
|  | 518 | // merging of aa vs. bw rects. | 
|  | 519 | static void test_rect_merging(skiatest::Reporter* reporter) { | 
|  | 520 |  | 
|  | 521 | SkRect overlapLeft  = SkRect::MakeLTRB(10, 10, 50, 50); | 
|  | 522 | SkRect overlapRight = SkRect::MakeLTRB(40, 40, 80, 80); | 
|  | 523 |  | 
|  | 524 | SkRect nestedParent = SkRect::MakeLTRB(10, 10, 90, 90); | 
|  | 525 | SkRect nestedChild  = SkRect::MakeLTRB(40, 40, 60, 60); | 
|  | 526 |  | 
|  | 527 | SkRect bound; | 
|  | 528 | SkClipStack::BoundsType type; | 
|  | 529 | bool isIntersectionOfRects; | 
|  | 530 |  | 
|  | 531 | // all bw overlapping - should merge | 
|  | 532 | { | 
|  | 533 | SkClipStack stack; | 
|  | 534 |  | 
|  | 535 | stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, false); | 
|  | 536 |  | 
|  | 537 | stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, false); | 
|  | 538 |  | 
|  | 539 | REPORTER_ASSERT(reporter, 1 == count(stack)); | 
|  | 540 |  | 
|  | 541 | stack.getBounds(&bound, &type, &isIntersectionOfRects); | 
|  | 542 |  | 
|  | 543 | REPORTER_ASSERT(reporter, isIntersectionOfRects); | 
|  | 544 | } | 
|  | 545 |  | 
|  | 546 | // all aa overlapping - should merge | 
|  | 547 | { | 
|  | 548 | SkClipStack stack; | 
|  | 549 |  | 
|  | 550 | stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, true); | 
|  | 551 |  | 
|  | 552 | stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, true); | 
|  | 553 |  | 
|  | 554 | REPORTER_ASSERT(reporter, 1 == count(stack)); | 
|  | 555 |  | 
|  | 556 | stack.getBounds(&bound, &type, &isIntersectionOfRects); | 
|  | 557 |  | 
|  | 558 | REPORTER_ASSERT(reporter, isIntersectionOfRects); | 
|  | 559 | } | 
|  | 560 |  | 
|  | 561 | // mixed overlapping - should _not_ merge | 
|  | 562 | { | 
|  | 563 | SkClipStack stack; | 
|  | 564 |  | 
|  | 565 | stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, true); | 
|  | 566 |  | 
|  | 567 | stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, false); | 
|  | 568 |  | 
|  | 569 | REPORTER_ASSERT(reporter, 2 == count(stack)); | 
|  | 570 |  | 
|  | 571 | stack.getBounds(&bound, &type, &isIntersectionOfRects); | 
|  | 572 |  | 
|  | 573 | REPORTER_ASSERT(reporter, !isIntersectionOfRects); | 
|  | 574 | } | 
|  | 575 |  | 
|  | 576 | // mixed nested (bw inside aa) - should merge | 
|  | 577 | { | 
|  | 578 | SkClipStack stack; | 
|  | 579 |  | 
|  | 580 | stack.clipDevRect(nestedParent, SkRegion::kReplace_Op, true); | 
|  | 581 |  | 
|  | 582 | stack.clipDevRect(nestedChild, SkRegion::kIntersect_Op, false); | 
|  | 583 |  | 
|  | 584 | REPORTER_ASSERT(reporter, 1 == count(stack)); | 
|  | 585 |  | 
|  | 586 | stack.getBounds(&bound, &type, &isIntersectionOfRects); | 
|  | 587 |  | 
|  | 588 | REPORTER_ASSERT(reporter, isIntersectionOfRects); | 
|  | 589 | } | 
|  | 590 |  | 
|  | 591 | // mixed nested (aa inside bw) - should merge | 
|  | 592 | { | 
|  | 593 | SkClipStack stack; | 
|  | 594 |  | 
|  | 595 | stack.clipDevRect(nestedParent, SkRegion::kReplace_Op, false); | 
|  | 596 |  | 
|  | 597 | stack.clipDevRect(nestedChild, SkRegion::kIntersect_Op, true); | 
|  | 598 |  | 
|  | 599 | REPORTER_ASSERT(reporter, 1 == count(stack)); | 
|  | 600 |  | 
|  | 601 | stack.getBounds(&bound, &type, &isIntersectionOfRects); | 
|  | 602 |  | 
|  | 603 | REPORTER_ASSERT(reporter, isIntersectionOfRects); | 
|  | 604 | } | 
|  | 605 |  | 
|  | 606 | // reverse nested (aa inside bw) - should _not_ merge | 
|  | 607 | { | 
|  | 608 | SkClipStack stack; | 
|  | 609 |  | 
|  | 610 | stack.clipDevRect(nestedChild, SkRegion::kReplace_Op, false); | 
|  | 611 |  | 
|  | 612 | stack.clipDevRect(nestedParent, SkRegion::kIntersect_Op, true); | 
|  | 613 |  | 
|  | 614 | REPORTER_ASSERT(reporter, 2 == count(stack)); | 
|  | 615 |  | 
|  | 616 | stack.getBounds(&bound, &type, &isIntersectionOfRects); | 
|  | 617 |  | 
|  | 618 | REPORTER_ASSERT(reporter, !isIntersectionOfRects); | 
|  | 619 | } | 
|  | 620 | } | 
| robertphillips@google.com | cc6493b | 2012-07-26 18:39:13 +0000 | [diff] [blame] | 621 |  | 
| junov@chromium.org | 8cdf0f5 | 2012-12-12 17:58:15 +0000 | [diff] [blame] | 622 | static void test_quickContains(skiatest::Reporter* reporter) { | 
|  | 623 | SkRect testRect = SkRect::MakeLTRB(10, 10, 40, 40); | 
|  | 624 | SkRect insideRect = SkRect::MakeLTRB(20, 20, 30, 30); | 
|  | 625 | SkRect intersectingRect = SkRect::MakeLTRB(25, 25, 50, 50); | 
|  | 626 | SkRect outsideRect = SkRect::MakeLTRB(0, 0, 50, 50); | 
|  | 627 | SkRect nonIntersectingRect = SkRect::MakeLTRB(100, 100, 110, 110); | 
|  | 628 |  | 
|  | 629 | SkPath insideCircle; | 
|  | 630 | insideCircle.addCircle(25, 25, 5); | 
|  | 631 | SkPath intersectingCircle; | 
|  | 632 | intersectingCircle.addCircle(25, 40, 10); | 
|  | 633 | SkPath outsideCircle; | 
|  | 634 | outsideCircle.addCircle(25, 25, 50); | 
|  | 635 | SkPath nonIntersectingCircle; | 
|  | 636 | nonIntersectingCircle.addCircle(100, 100, 5); | 
|  | 637 |  | 
|  | 638 | { | 
|  | 639 | SkClipStack stack; | 
|  | 640 | stack.clipDevRect(outsideRect, SkRegion::kDifference_Op, false); | 
|  | 641 | // return false because quickContains currently does not care for kDifference_Op | 
|  | 642 | REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); | 
|  | 643 | } | 
| skia.committer@gmail.com | 306ab9d | 2012-12-13 02:01:33 +0000 | [diff] [blame] | 644 |  | 
| junov@chromium.org | 8cdf0f5 | 2012-12-12 17:58:15 +0000 | [diff] [blame] | 645 | // Replace Op tests | 
|  | 646 | { | 
|  | 647 | SkClipStack stack; | 
|  | 648 | stack.clipDevRect(outsideRect, SkRegion::kReplace_Op, false); | 
|  | 649 | REPORTER_ASSERT(reporter, true == stack.quickContains(testRect)); | 
|  | 650 | } | 
|  | 651 |  | 
|  | 652 | { | 
|  | 653 | SkClipStack stack; | 
|  | 654 | stack.clipDevRect(insideRect, SkRegion::kIntersect_Op, false); | 
|  | 655 | stack.save(); // To prevent in-place substitution by replace OP | 
|  | 656 | stack.clipDevRect(outsideRect, SkRegion::kReplace_Op, false); | 
|  | 657 | REPORTER_ASSERT(reporter, true == stack.quickContains(testRect)); | 
|  | 658 | stack.restore(); | 
|  | 659 | } | 
|  | 660 |  | 
|  | 661 | { | 
|  | 662 | SkClipStack stack; | 
|  | 663 | stack.clipDevRect(outsideRect, SkRegion::kIntersect_Op, false); | 
|  | 664 | stack.save(); // To prevent in-place substitution by replace OP | 
|  | 665 | stack.clipDevRect(insideRect, SkRegion::kReplace_Op, false); | 
|  | 666 | REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); | 
|  | 667 | stack.restore(); | 
|  | 668 | } | 
|  | 669 |  | 
|  | 670 | // Verify proper traversal of multi-element clip | 
|  | 671 | { | 
|  | 672 | SkClipStack stack; | 
|  | 673 | stack.clipDevRect(insideRect, SkRegion::kIntersect_Op, false); | 
|  | 674 | // Use a path for second clip to prevent in-place intersection | 
|  | 675 | stack.clipDevPath(outsideCircle, SkRegion::kIntersect_Op, false); | 
|  | 676 | REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); | 
|  | 677 | } | 
|  | 678 |  | 
|  | 679 | // Intersect Op tests with rectangles | 
|  | 680 | { | 
|  | 681 | SkClipStack stack; | 
|  | 682 | stack.clipDevRect(outsideRect, SkRegion::kIntersect_Op, false); | 
|  | 683 | REPORTER_ASSERT(reporter, true == stack.quickContains(testRect)); | 
|  | 684 | } | 
|  | 685 |  | 
|  | 686 | { | 
|  | 687 | SkClipStack stack; | 
|  | 688 | stack.clipDevRect(insideRect, SkRegion::kIntersect_Op, false); | 
|  | 689 | REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); | 
|  | 690 | } | 
|  | 691 |  | 
|  | 692 | { | 
|  | 693 | SkClipStack stack; | 
|  | 694 | stack.clipDevRect(intersectingRect, SkRegion::kIntersect_Op, false); | 
|  | 695 | REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); | 
|  | 696 | } | 
|  | 697 |  | 
|  | 698 | { | 
|  | 699 | SkClipStack stack; | 
|  | 700 | stack.clipDevRect(nonIntersectingRect, SkRegion::kIntersect_Op, false); | 
|  | 701 | REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); | 
|  | 702 | } | 
|  | 703 |  | 
|  | 704 | // Intersect Op tests with circle paths | 
|  | 705 | { | 
|  | 706 | SkClipStack stack; | 
|  | 707 | stack.clipDevPath(outsideCircle, SkRegion::kIntersect_Op, false); | 
|  | 708 | REPORTER_ASSERT(reporter, true == stack.quickContains(testRect)); | 
|  | 709 | } | 
|  | 710 |  | 
|  | 711 | { | 
|  | 712 | SkClipStack stack; | 
|  | 713 | stack.clipDevPath(insideCircle, SkRegion::kIntersect_Op, false); | 
|  | 714 | REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); | 
|  | 715 | } | 
|  | 716 |  | 
|  | 717 | { | 
|  | 718 | SkClipStack stack; | 
|  | 719 | stack.clipDevPath(intersectingCircle, SkRegion::kIntersect_Op, false); | 
|  | 720 | REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); | 
|  | 721 | } | 
|  | 722 |  | 
|  | 723 | { | 
|  | 724 | SkClipStack stack; | 
|  | 725 | stack.clipDevPath(nonIntersectingCircle, SkRegion::kIntersect_Op, false); | 
|  | 726 | REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); | 
|  | 727 | } | 
|  | 728 |  | 
|  | 729 | // Intersect Op tests with inverse filled rectangles | 
|  | 730 | { | 
|  | 731 | SkClipStack stack; | 
|  | 732 | SkPath path; | 
|  | 733 | path.addRect(outsideRect); | 
|  | 734 | path.toggleInverseFillType(); | 
|  | 735 | stack.clipDevPath(path, SkRegion::kIntersect_Op, false); | 
|  | 736 | REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); | 
|  | 737 | } | 
|  | 738 |  | 
|  | 739 | { | 
|  | 740 | SkClipStack stack; | 
|  | 741 | SkPath path; | 
|  | 742 | path.addRect(insideRect); | 
|  | 743 | path.toggleInverseFillType(); | 
|  | 744 | stack.clipDevPath(path, SkRegion::kIntersect_Op, false); | 
|  | 745 | REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); | 
|  | 746 | } | 
|  | 747 |  | 
|  | 748 | { | 
|  | 749 | SkClipStack stack; | 
|  | 750 | SkPath path; | 
|  | 751 | path.addRect(intersectingRect); | 
|  | 752 | path.toggleInverseFillType(); | 
|  | 753 | stack.clipDevPath(path, SkRegion::kIntersect_Op, false); | 
|  | 754 | REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); | 
|  | 755 | } | 
|  | 756 |  | 
|  | 757 | { | 
|  | 758 | SkClipStack stack; | 
|  | 759 | SkPath path; | 
|  | 760 | path.addRect(nonIntersectingRect); | 
|  | 761 | path.toggleInverseFillType(); | 
|  | 762 | stack.clipDevPath(path, SkRegion::kIntersect_Op, false); | 
|  | 763 | REPORTER_ASSERT(reporter, true == stack.quickContains(testRect)); | 
|  | 764 | } | 
|  | 765 |  | 
|  | 766 | // Intersect Op tests with inverse filled circles | 
|  | 767 | { | 
|  | 768 | SkClipStack stack; | 
|  | 769 | SkPath path = outsideCircle; | 
|  | 770 | path.toggleInverseFillType(); | 
|  | 771 | stack.clipDevPath(path, SkRegion::kIntersect_Op, false); | 
|  | 772 | REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); | 
|  | 773 | } | 
|  | 774 |  | 
|  | 775 | { | 
|  | 776 | SkClipStack stack; | 
|  | 777 | SkPath path = insideCircle; | 
|  | 778 | path.toggleInverseFillType(); | 
|  | 779 | stack.clipDevPath(path, SkRegion::kIntersect_Op, false); | 
|  | 780 | REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); | 
|  | 781 | } | 
|  | 782 |  | 
|  | 783 | { | 
|  | 784 | SkClipStack stack; | 
|  | 785 | SkPath path = intersectingCircle; | 
|  | 786 | path.toggleInverseFillType(); | 
|  | 787 | stack.clipDevPath(path, SkRegion::kIntersect_Op, false); | 
|  | 788 | REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); | 
|  | 789 | } | 
|  | 790 |  | 
|  | 791 | { | 
|  | 792 | SkClipStack stack; | 
|  | 793 | SkPath path = nonIntersectingCircle; | 
|  | 794 | path.toggleInverseFillType(); | 
|  | 795 | stack.clipDevPath(path, SkRegion::kIntersect_Op, false); | 
|  | 796 | REPORTER_ASSERT(reporter, true == stack.quickContains(testRect)); | 
|  | 797 | } | 
|  | 798 | } | 
|  | 799 |  | 
| csmartdalton | d50e240 | 2016-07-22 08:39:06 -0700 | [diff] [blame] | 800 | static void set_region_to_stack(const SkClipStack& stack, const SkIRect& bounds, SkRegion* region) { | 
|  | 801 | region->setRect(bounds); | 
|  | 802 | SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart); | 
|  | 803 | while (const SkClipStack::Element *element = iter.next()) { | 
|  | 804 | SkRegion elemRegion; | 
|  | 805 | SkRegion boundsRgn(bounds); | 
|  | 806 | SkPath path; | 
|  | 807 |  | 
|  | 808 | switch (element->getType()) { | 
|  | 809 | case SkClipStack::Element::kEmpty_Type: | 
|  | 810 | elemRegion.setEmpty(); | 
|  | 811 | break; | 
|  | 812 | default: | 
|  | 813 | element->asPath(&path); | 
|  | 814 | elemRegion.setPath(path, boundsRgn); | 
|  | 815 | break; | 
|  | 816 | } | 
|  | 817 | region->op(elemRegion, element->getOp()); | 
|  | 818 | } | 
|  | 819 | } | 
|  | 820 |  | 
|  | 821 | static void test_invfill_diff_bug(skiatest::Reporter* reporter) { | 
|  | 822 | SkClipStack stack; | 
|  | 823 | stack.clipDevRect({10, 10, 20, 20}, SkRegion::kIntersect_Op, false); | 
|  | 824 |  | 
|  | 825 | SkPath path; | 
|  | 826 | path.addRect({30, 10, 40, 20}); | 
|  | 827 | path.setFillType(SkPath::kInverseWinding_FillType); | 
|  | 828 | stack.clipDevPath(path, SkRegion::kDifference_Op, false); | 
|  | 829 |  | 
|  | 830 | REPORTER_ASSERT(reporter, SkClipStack::kEmptyGenID == stack.getTopmostGenID()); | 
|  | 831 |  | 
|  | 832 | SkRect stackBounds; | 
|  | 833 | SkClipStack::BoundsType stackBoundsType; | 
|  | 834 | stack.getBounds(&stackBounds, &stackBoundsType); | 
|  | 835 |  | 
|  | 836 | REPORTER_ASSERT(reporter, stackBounds.isEmpty()); | 
|  | 837 | REPORTER_ASSERT(reporter, SkClipStack::kNormal_BoundsType == stackBoundsType); | 
|  | 838 |  | 
|  | 839 | SkRegion region; | 
|  | 840 | set_region_to_stack(stack, {0, 0, 50, 30}, ®ion); | 
|  | 841 |  | 
|  | 842 | REPORTER_ASSERT(reporter, region.isEmpty()); | 
|  | 843 | } | 
|  | 844 |  | 
| bsalomon@google.com | 51a6286 | 2012-11-26 21:19:43 +0000 | [diff] [blame] | 845 | /////////////////////////////////////////////////////////////////////////////////////////////////// | 
|  | 846 |  | 
| bsalomon@google.com | a4e13c8 | 2012-11-26 21:38:37 +0000 | [diff] [blame] | 847 | #if SK_SUPPORT_GPU | 
| bsalomon@google.com | 705e840 | 2012-11-27 15:43:57 +0000 | [diff] [blame] | 848 | // Functions that add a shape to the clip stack. The shape is computed from a rectangle. | 
|  | 849 | // AA is always disabled since the clip stack reducer can cause changes in aa rasterization of the | 
|  | 850 | // stack. A fractional edge repeated in different elements may be rasterized fewer times using the | 
|  | 851 | // reduced stack. | 
|  | 852 | typedef void (*AddElementFunc) (const SkRect& rect, | 
|  | 853 | bool invert, | 
|  | 854 | SkRegion::Op op, | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 855 | SkClipStack* stack, | 
|  | 856 | bool doAA); | 
| bsalomon@google.com | a4e13c8 | 2012-11-26 21:38:37 +0000 | [diff] [blame] | 857 |  | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 858 | static void add_round_rect(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack, | 
|  | 859 | bool doAA) { | 
| bsalomon@google.com | 51a6286 | 2012-11-26 21:19:43 +0000 | [diff] [blame] | 860 | SkScalar rx = rect.width() / 10; | 
| bsalomon@google.com | 705e840 | 2012-11-27 15:43:57 +0000 | [diff] [blame] | 861 | SkScalar ry = rect.height() / 20; | 
| bsalomon@google.com | 705e840 | 2012-11-27 15:43:57 +0000 | [diff] [blame] | 862 | if (invert) { | 
| commit-bot@chromium.org | e5b2af9 | 2014-02-16 13:25:24 +0000 | [diff] [blame] | 863 | SkPath path; | 
|  | 864 | path.addRoundRect(rect, rx, ry); | 
| bsalomon@google.com | 705e840 | 2012-11-27 15:43:57 +0000 | [diff] [blame] | 865 | path.setFillType(SkPath::kInverseWinding_FillType); | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 866 | stack->clipDevPath(path, op, doAA); | 
| commit-bot@chromium.org | e5b2af9 | 2014-02-16 13:25:24 +0000 | [diff] [blame] | 867 | } else { | 
|  | 868 | SkRRect rrect; | 
|  | 869 | rrect.setRectXY(rect, rx, ry); | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 870 | stack->clipDevRRect(rrect, op, doAA); | 
| bsalomon@google.com | 705e840 | 2012-11-27 15:43:57 +0000 | [diff] [blame] | 871 | } | 
| bsalomon@google.com | 51a6286 | 2012-11-26 21:19:43 +0000 | [diff] [blame] | 872 | }; | 
|  | 873 |  | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 874 | static void add_rect(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack, | 
|  | 875 | bool doAA) { | 
| bsalomon@google.com | 705e840 | 2012-11-27 15:43:57 +0000 | [diff] [blame] | 876 | if (invert) { | 
|  | 877 | SkPath path; | 
|  | 878 | path.addRect(rect); | 
|  | 879 | path.setFillType(SkPath::kInverseWinding_FillType); | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 880 | stack->clipDevPath(path, op, doAA); | 
| bsalomon@google.com | 705e840 | 2012-11-27 15:43:57 +0000 | [diff] [blame] | 881 | } else { | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 882 | stack->clipDevRect(rect, op, doAA); | 
| bsalomon@google.com | 705e840 | 2012-11-27 15:43:57 +0000 | [diff] [blame] | 883 | } | 
| bsalomon@google.com | 51a6286 | 2012-11-26 21:19:43 +0000 | [diff] [blame] | 884 | }; | 
|  | 885 |  | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 886 | static void add_oval(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack, | 
|  | 887 | bool doAA) { | 
| bsalomon@google.com | 51a6286 | 2012-11-26 21:19:43 +0000 | [diff] [blame] | 888 | SkPath path; | 
|  | 889 | path.addOval(rect); | 
| bsalomon@google.com | 705e840 | 2012-11-27 15:43:57 +0000 | [diff] [blame] | 890 | if (invert) { | 
|  | 891 | path.setFillType(SkPath::kInverseWinding_FillType); | 
|  | 892 | } | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 893 | stack->clipDevPath(path, op, doAA); | 
| bsalomon@google.com | 51a6286 | 2012-11-26 21:19:43 +0000 | [diff] [blame] | 894 | }; | 
|  | 895 |  | 
| bsalomon@google.com | 8182fa0 | 2012-12-04 14:06:06 +0000 | [diff] [blame] | 896 | static void add_elem_to_stack(const SkClipStack::Element& element, SkClipStack* stack) { | 
|  | 897 | switch (element.getType()) { | 
|  | 898 | case SkClipStack::Element::kRect_Type: | 
|  | 899 | stack->clipDevRect(element.getRect(), element.getOp(), element.isAA()); | 
|  | 900 | break; | 
| commit-bot@chromium.org | e5b2af9 | 2014-02-16 13:25:24 +0000 | [diff] [blame] | 901 | case SkClipStack::Element::kRRect_Type: | 
|  | 902 | stack->clipDevRRect(element.getRRect(), element.getOp(), element.isAA()); | 
|  | 903 | break; | 
| bsalomon@google.com | 8182fa0 | 2012-12-04 14:06:06 +0000 | [diff] [blame] | 904 | case SkClipStack::Element::kPath_Type: | 
|  | 905 | stack->clipDevPath(element.getPath(), element.getOp(), element.isAA()); | 
|  | 906 | break; | 
|  | 907 | case SkClipStack::Element::kEmpty_Type: | 
|  | 908 | SkDEBUGFAIL("Why did the reducer produce an explicit empty."); | 
|  | 909 | stack->clipEmpty(); | 
|  | 910 | break; | 
| bsalomon@google.com | 51a6286 | 2012-11-26 21:19:43 +0000 | [diff] [blame] | 911 | } | 
|  | 912 | } | 
|  | 913 |  | 
| bsalomon@google.com | 51a6286 | 2012-11-26 21:19:43 +0000 | [diff] [blame] | 914 | static void test_reduced_clip_stack(skiatest::Reporter* reporter) { | 
|  | 915 | // We construct random clip stacks, reduce them, and then rasterize both versions to verify that | 
| skia.committer@gmail.com | 8ccf590 | 2012-11-27 02:01:19 +0000 | [diff] [blame] | 916 | // they are equal. | 
| bsalomon@google.com | 51a6286 | 2012-11-26 21:19:43 +0000 | [diff] [blame] | 917 |  | 
|  | 918 | // All the clip elements will be contained within these bounds. | 
|  | 919 | static const SkRect kBounds = SkRect::MakeWH(100, 100); | 
|  | 920 |  | 
|  | 921 | enum { | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 922 | kNumTests = 250, | 
| bsalomon@google.com | 51a6286 | 2012-11-26 21:19:43 +0000 | [diff] [blame] | 923 | kMinElemsPerTest = 1, | 
|  | 924 | kMaxElemsPerTest = 50, | 
|  | 925 | }; | 
|  | 926 |  | 
|  | 927 | // min/max size of a clip element as a fraction of kBounds. | 
|  | 928 | static const SkScalar kMinElemSizeFrac = SK_Scalar1 / 5; | 
|  | 929 | static const SkScalar kMaxElemSizeFrac = SK_Scalar1; | 
|  | 930 |  | 
|  | 931 | static const SkRegion::Op kOps[] = { | 
|  | 932 | SkRegion::kDifference_Op, | 
|  | 933 | SkRegion::kIntersect_Op, | 
|  | 934 | SkRegion::kUnion_Op, | 
|  | 935 | SkRegion::kXOR_Op, | 
|  | 936 | SkRegion::kReverseDifference_Op, | 
|  | 937 | SkRegion::kReplace_Op, | 
|  | 938 | }; | 
|  | 939 |  | 
|  | 940 | // Replace operations short-circuit the optimizer. We want to make sure that we test this code | 
|  | 941 | // path a little bit but we don't want it to prevent us from testing many longer traversals in | 
|  | 942 | // the optimizer. | 
|  | 943 | static const int kReplaceDiv = 4 * kMaxElemsPerTest; | 
|  | 944 |  | 
| bsalomon@google.com | 705e840 | 2012-11-27 15:43:57 +0000 | [diff] [blame] | 945 | // We want to test inverse fills. However, they are quite rare in practice so don't over do it. | 
|  | 946 | static const SkScalar kFractionInverted = SK_Scalar1 / kMaxElemsPerTest; | 
|  | 947 |  | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 948 | static const SkScalar kFractionAntialiased = 0.25; | 
|  | 949 |  | 
| bsalomon@google.com | 51a6286 | 2012-11-26 21:19:43 +0000 | [diff] [blame] | 950 | static const AddElementFunc kElementFuncs[] = { | 
|  | 951 | add_rect, | 
|  | 952 | add_round_rect, | 
|  | 953 | add_oval, | 
|  | 954 | }; | 
|  | 955 |  | 
| commit-bot@chromium.org | e0e7cfe | 2013-09-09 20:09:12 +0000 | [diff] [blame] | 956 | SkRandom r; | 
| bsalomon@google.com | 51a6286 | 2012-11-26 21:19:43 +0000 | [diff] [blame] | 957 |  | 
|  | 958 | for (int i = 0; i < kNumTests; ++i) { | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 959 | SkString testCase; | 
|  | 960 | testCase.printf("Iteration %d", i); | 
|  | 961 |  | 
| bsalomon@google.com | 51a6286 | 2012-11-26 21:19:43 +0000 | [diff] [blame] | 962 | // Randomly generate a clip stack. | 
|  | 963 | SkClipStack stack; | 
|  | 964 | int numElems = r.nextRangeU(kMinElemsPerTest, kMaxElemsPerTest); | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 965 | bool doAA = r.nextBiasedBool(kFractionAntialiased); | 
| bsalomon@google.com | 51a6286 | 2012-11-26 21:19:43 +0000 | [diff] [blame] | 966 | for (int e = 0; e < numElems; ++e) { | 
|  | 967 | SkRegion::Op op = kOps[r.nextULessThan(SK_ARRAY_COUNT(kOps))]; | 
|  | 968 | if (op == SkRegion::kReplace_Op) { | 
|  | 969 | if (r.nextU() % kReplaceDiv) { | 
|  | 970 | --e; | 
|  | 971 | continue; | 
|  | 972 | } | 
|  | 973 | } | 
| skia.committer@gmail.com | 8ccf590 | 2012-11-27 02:01:19 +0000 | [diff] [blame] | 974 |  | 
| bsalomon@google.com | 51a6286 | 2012-11-26 21:19:43 +0000 | [diff] [blame] | 975 | // saves can change the clip stack behavior when an element is added. | 
|  | 976 | bool doSave = r.nextBool(); | 
| skia.committer@gmail.com | 8ccf590 | 2012-11-27 02:01:19 +0000 | [diff] [blame] | 977 |  | 
| bsalomon@google.com | 51a6286 | 2012-11-26 21:19:43 +0000 | [diff] [blame] | 978 | SkSize size = SkSize::Make( | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 979 | SkScalarMul(kBounds.width(), r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac)), | 
|  | 980 | SkScalarMul(kBounds.height(), r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac))); | 
| bsalomon@google.com | 51a6286 | 2012-11-26 21:19:43 +0000 | [diff] [blame] | 981 |  | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 982 | SkPoint xy = {r.nextRangeScalar(kBounds.fLeft, kBounds.fRight - size.fWidth), | 
|  | 983 | r.nextRangeScalar(kBounds.fTop, kBounds.fBottom - size.fHeight)}; | 
| bsalomon@google.com | 51a6286 | 2012-11-26 21:19:43 +0000 | [diff] [blame] | 984 |  | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 985 | SkRect rect; | 
|  | 986 | if (doAA) { | 
|  | 987 | rect.setXYWH(xy.fX, xy.fY, size.fWidth, size.fHeight); | 
|  | 988 | if (GrClip::IsPixelAligned(rect)) { | 
|  | 989 | // Don't create an element that may accidentally become not antialiased. | 
|  | 990 | rect.outset(0.5f, 0.5f); | 
|  | 991 | } | 
|  | 992 | SkASSERT(!GrClip::IsPixelAligned(rect)); | 
|  | 993 | } else { | 
|  | 994 | rect.setXYWH(SkScalarFloorToScalar(xy.fX), | 
|  | 995 | SkScalarFloorToScalar(xy.fY), | 
|  | 996 | SkScalarCeilToScalar(size.fWidth), | 
|  | 997 | SkScalarCeilToScalar(size.fHeight)); | 
|  | 998 | } | 
| bsalomon@google.com | 51a6286 | 2012-11-26 21:19:43 +0000 | [diff] [blame] | 999 |  | 
| bsalomon@google.com | 705e840 | 2012-11-27 15:43:57 +0000 | [diff] [blame] | 1000 | bool invert = r.nextBiasedBool(kFractionInverted); | 
| commit-bot@chromium.org | e5b2af9 | 2014-02-16 13:25:24 +0000 | [diff] [blame] | 1001 |  | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 1002 | kElementFuncs[r.nextULessThan(SK_ARRAY_COUNT(kElementFuncs))](rect, invert, op, &stack, | 
|  | 1003 | doAA); | 
| bsalomon@google.com | 51a6286 | 2012-11-26 21:19:43 +0000 | [diff] [blame] | 1004 | if (doSave) { | 
|  | 1005 | stack.save(); | 
|  | 1006 | } | 
|  | 1007 | } | 
|  | 1008 |  | 
| csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame^] | 1009 | // Get the reduced version of the stack. | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 1010 | SkRect queryBounds = kBounds; | 
|  | 1011 | queryBounds.outset(kBounds.width() / 2, kBounds.height() / 2); | 
| csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame^] | 1012 | const GrReducedClip reduced(stack, queryBounds); | 
| bsalomon@google.com | a444430 | 2012-12-04 15:22:12 +0000 | [diff] [blame] | 1013 |  | 
| csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame^] | 1014 | REPORTER_ASSERT_MESSAGE(reporter, SkClipStack::kInvalidGenID != reduced.genID(), | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 1015 | testCase.c_str()); | 
|  | 1016 |  | 
| csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame^] | 1017 | if (!reduced.elements().isEmpty()) { | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 1018 | SkRect stackBounds; | 
|  | 1019 | SkClipStack::BoundsType stackBoundsType; | 
|  | 1020 | stack.getBounds(&stackBounds, &stackBoundsType); | 
|  | 1021 | if (SkClipStack::kNormal_BoundsType == stackBoundsType) { | 
|  | 1022 | // Unless GrReducedClip starts doing some heroic tightening of the clip bounds, this | 
|  | 1023 | // will be true since the stack bounds are completely contained inside the query. | 
| csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame^] | 1024 | REPORTER_ASSERT_MESSAGE(reporter, GrClip::IsInsideClip(reduced.iBounds(), stackBounds), | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 1025 | testCase.c_str()); | 
|  | 1026 | } | 
| csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame^] | 1027 | REPORTER_ASSERT_MESSAGE(reporter, reduced.requiresAA() == doAA, testCase.c_str()); | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 1028 | } | 
| commit-bot@chromium.org | d3e5842 | 2013-11-05 15:03:08 +0000 | [diff] [blame] | 1029 |  | 
| bsalomon@google.com | 51a6286 | 2012-11-26 21:19:43 +0000 | [diff] [blame] | 1030 | // Build a new clip stack based on the reduced clip elements | 
|  | 1031 | SkClipStack reducedStack; | 
| csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame^] | 1032 | if (GrReducedClip::InitialState::kAllOut == reduced.initialState()) { | 
| bsalomon@google.com | 51a6286 | 2012-11-26 21:19:43 +0000 | [diff] [blame] | 1033 | // whether the result is bounded or not, the whole plane should start outside the clip. | 
|  | 1034 | reducedStack.clipEmpty(); | 
|  | 1035 | } | 
| csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame^] | 1036 | for (ElementList::Iter iter(reduced.elements()); iter.get(); iter.next()) { | 
| bsalomon@google.com | 8182fa0 | 2012-12-04 14:06:06 +0000 | [diff] [blame] | 1037 | add_elem_to_stack(*iter.get(), &reducedStack); | 
| bsalomon@google.com | 51a6286 | 2012-11-26 21:19:43 +0000 | [diff] [blame] | 1038 | } | 
| bsalomon@google.com | 51a6286 | 2012-11-26 21:19:43 +0000 | [diff] [blame] | 1039 |  | 
| bsalomon@google.com | 34cd70a | 2012-12-06 14:23:20 +0000 | [diff] [blame] | 1040 | // GrReducedClipStack assumes that the final result is clipped to the returned bounds | 
| csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame^] | 1041 | reducedStack.clipDevRect(reduced.iBounds(), SkRegion::kIntersect_Op); | 
|  | 1042 | stack.clipDevRect(reduced.iBounds(), SkRegion::kIntersect_Op); | 
| bsalomon@google.com | 34cd70a | 2012-12-06 14:23:20 +0000 | [diff] [blame] | 1043 |  | 
| bsalomon@google.com | 51a6286 | 2012-11-26 21:19:43 +0000 | [diff] [blame] | 1044 | // convert both the original stack and reduced stack to SkRegions and see if they're equal | 
| bsalomon@google.com | 51a6286 | 2012-11-26 21:19:43 +0000 | [diff] [blame] | 1045 | SkRegion region; | 
| csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame^] | 1046 | set_region_to_stack(stack, reduced.iBounds(), ®ion); | 
| csmartdalton | d50e240 | 2016-07-22 08:39:06 -0700 | [diff] [blame] | 1047 |  | 
| bsalomon@google.com | 51a6286 | 2012-11-26 21:19:43 +0000 | [diff] [blame] | 1048 | SkRegion reducedRegion; | 
| csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame^] | 1049 | set_region_to_stack(reducedStack, reduced.iBounds(), &reducedRegion); | 
| bsalomon@google.com | 51a6286 | 2012-11-26 21:19:43 +0000 | [diff] [blame] | 1050 |  | 
| commit-bot@chromium.org | e5b2af9 | 2014-02-16 13:25:24 +0000 | [diff] [blame] | 1051 | REPORTER_ASSERT_MESSAGE(reporter, region == reducedRegion, testCase.c_str()); | 
| bsalomon@google.com | 51a6286 | 2012-11-26 21:19:43 +0000 | [diff] [blame] | 1052 | } | 
|  | 1053 | } | 
|  | 1054 |  | 
| halcanary | 4dbbd04 | 2016-06-07 17:21:10 -0700 | [diff] [blame] | 1055 | #ifdef SK_BUILD_FOR_WIN | 
| commit-bot@chromium.org | d3e5842 | 2013-11-05 15:03:08 +0000 | [diff] [blame] | 1056 | #define SUPPRESS_VISIBILITY_WARNING | 
|  | 1057 | #else | 
|  | 1058 | #define SUPPRESS_VISIBILITY_WARNING __attribute__((visibility("hidden"))) | 
|  | 1059 | #endif | 
|  | 1060 |  | 
|  | 1061 | static void test_reduced_clip_stack_genid(skiatest::Reporter* reporter) { | 
|  | 1062 | { | 
|  | 1063 | SkClipStack stack; | 
|  | 1064 | stack.clipDevRect(SkRect::MakeXYWH(0, 0, 100, 100), SkRegion::kReplace_Op, true); | 
|  | 1065 | stack.clipDevRect(SkRect::MakeXYWH(0, 0, SkScalar(50.3), SkScalar(50.3)), SkRegion::kReplace_Op, true); | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 1066 | SkRect bounds = SkRect::MakeXYWH(0, 0, 100, 100); | 
| commit-bot@chromium.org | d3e5842 | 2013-11-05 15:03:08 +0000 | [diff] [blame] | 1067 |  | 
| csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame^] | 1068 | const GrReducedClip reduced(stack, bounds); | 
| commit-bot@chromium.org | d3e5842 | 2013-11-05 15:03:08 +0000 | [diff] [blame] | 1069 |  | 
| csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame^] | 1070 | REPORTER_ASSERT(reporter, reduced.elements().count() == 1); | 
| commit-bot@chromium.org | d3e5842 | 2013-11-05 15:03:08 +0000 | [diff] [blame] | 1071 | // Clips will be cached based on the generation id. Make sure the gen id is valid. | 
| csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame^] | 1072 | REPORTER_ASSERT(reporter, SkClipStack::kInvalidGenID != reduced.genID()); | 
| commit-bot@chromium.org | d3e5842 | 2013-11-05 15:03:08 +0000 | [diff] [blame] | 1073 | } | 
|  | 1074 | { | 
|  | 1075 | SkClipStack stack; | 
|  | 1076 |  | 
|  | 1077 | // Create a clip with following 25.3, 25.3 boxes which are 25 apart: | 
|  | 1078 | //  A  B | 
|  | 1079 | //  C  D | 
|  | 1080 |  | 
|  | 1081 | stack.clipDevRect(SkRect::MakeXYWH(0, 0, SkScalar(25.3), SkScalar(25.3)), SkRegion::kReplace_Op, true); | 
|  | 1082 | int32_t genIDA = stack.getTopmostGenID(); | 
|  | 1083 | stack.clipDevRect(SkRect::MakeXYWH(50, 0, SkScalar(25.3), SkScalar(25.3)), SkRegion::kUnion_Op, true); | 
|  | 1084 | int32_t genIDB = stack.getTopmostGenID(); | 
|  | 1085 | stack.clipDevRect(SkRect::MakeXYWH(0, 50, SkScalar(25.3), SkScalar(25.3)), SkRegion::kUnion_Op, true); | 
|  | 1086 | int32_t genIDC = stack.getTopmostGenID(); | 
|  | 1087 | stack.clipDevRect(SkRect::MakeXYWH(50, 50, SkScalar(25.3), SkScalar(25.3)), SkRegion::kUnion_Op, true); | 
|  | 1088 | int32_t genIDD = stack.getTopmostGenID(); | 
|  | 1089 |  | 
|  | 1090 |  | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 1091 | #define IXYWH SkIRect::MakeXYWH | 
|  | 1092 | #define XYWH SkRect::MakeXYWH | 
| commit-bot@chromium.org | d3e5842 | 2013-11-05 15:03:08 +0000 | [diff] [blame] | 1093 |  | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 1094 | SkIRect stackBounds = IXYWH(0, 0, 76, 76); | 
| commit-bot@chromium.org | d3e5842 | 2013-11-05 15:03:08 +0000 | [diff] [blame] | 1095 |  | 
|  | 1096 | // The base test is to test each rect in two ways: | 
|  | 1097 | // 1) The box dimensions. (Should reduce to "all in", no elements). | 
|  | 1098 | // 2) A bit over the box dimensions. | 
|  | 1099 | // In the case 2, test that the generation id is what is expected. | 
|  | 1100 | // The rects are of fractional size so that case 2 never gets optimized to an empty element | 
|  | 1101 | // list. | 
|  | 1102 |  | 
|  | 1103 | // Not passing in tighter bounds is tested for consistency. | 
|  | 1104 | static const struct SUPPRESS_VISIBILITY_WARNING { | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 1105 | SkRect testBounds; | 
| commit-bot@chromium.org | d3e5842 | 2013-11-05 15:03:08 +0000 | [diff] [blame] | 1106 | int reducedClipCount; | 
|  | 1107 | int32_t reducedGenID; | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 1108 | InitialState initialState; | 
|  | 1109 | SkIRect clipIRect; | 
| commit-bot@chromium.org | d3e5842 | 2013-11-05 15:03:08 +0000 | [diff] [blame] | 1110 | // parameter. | 
|  | 1111 | } testCases[] = { | 
| csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame^] | 1112 |  | 
| commit-bot@chromium.org | d3e5842 | 2013-11-05 15:03:08 +0000 | [diff] [blame] | 1113 | // Rect A. | 
| csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame^] | 1114 | { XYWH(0, 0, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 0, 25, 25) }, | 
|  | 1115 | { XYWH(0.1f, 0.1f, 25.1f, 25.1f), 0, SkClipStack::kWideOpenGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 0, 26, 26) }, | 
|  | 1116 | { XYWH(0, 0, 27, 27), 1, genIDA, GrReducedClip::InitialState::kAllOut, IXYWH(0, 0, 27, 27)}, | 
| commit-bot@chromium.org | d3e5842 | 2013-11-05 15:03:08 +0000 | [diff] [blame] | 1117 |  | 
|  | 1118 | // Rect B. | 
| csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame^] | 1119 | { XYWH(50, 0, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 0, 25, 25) }, | 
|  | 1120 | { XYWH(50, 0, 25.3f, 25.3f), 0, SkClipStack::kWideOpenGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 0, 26, 26) }, | 
|  | 1121 | { XYWH(50, 0, 27, 27), 1, genIDB, GrReducedClip::InitialState::kAllOut, IXYWH(50, 0, 26, 27) }, | 
| commit-bot@chromium.org | d3e5842 | 2013-11-05 15:03:08 +0000 | [diff] [blame] | 1122 |  | 
|  | 1123 | // Rect C. | 
| csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame^] | 1124 | { XYWH(0, 50, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 50, 25, 25) }, | 
|  | 1125 | { XYWH(0.2f, 50.1f, 25.1f, 25.2f), 0, SkClipStack::kWideOpenGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 50, 26, 26) }, | 
|  | 1126 | { XYWH(0, 50, 27, 27), 1, genIDC, GrReducedClip::InitialState::kAllOut, IXYWH(0, 50, 27, 26) }, | 
| commit-bot@chromium.org | d3e5842 | 2013-11-05 15:03:08 +0000 | [diff] [blame] | 1127 |  | 
|  | 1128 | // Rect D. | 
| csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame^] | 1129 | { XYWH(50, 50, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 50, 25, 25)}, | 
|  | 1130 | { XYWH(50.3f, 50.3f, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 50, 26, 26)}, | 
|  | 1131 | { XYWH(50, 50, 27, 27), 1, genIDD, GrReducedClip::InitialState::kAllOut,  IXYWH(50, 50, 26, 26)}, | 
| commit-bot@chromium.org | d3e5842 | 2013-11-05 15:03:08 +0000 | [diff] [blame] | 1132 |  | 
|  | 1133 | // Other tests: | 
| csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame^] | 1134 | { XYWH(0, 0, 100, 100), 4, genIDD, GrReducedClip::InitialState::kAllOut, stackBounds }, | 
| commit-bot@chromium.org | d3e5842 | 2013-11-05 15:03:08 +0000 | [diff] [blame] | 1135 |  | 
|  | 1136 | // Rect in the middle, touches none. | 
| csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame^] | 1137 | { XYWH(26, 26, 24, 24), 0, SkClipStack::kEmptyGenID, GrReducedClip::InitialState::kAllOut, IXYWH(26, 26, 24, 24) }, | 
| commit-bot@chromium.org | d3e5842 | 2013-11-05 15:03:08 +0000 | [diff] [blame] | 1138 |  | 
|  | 1139 | // Rect in the middle, touches all the rects. GenID is the last rect. | 
| csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame^] | 1140 | { XYWH(24, 24, 27, 27), 4, genIDD, GrReducedClip::InitialState::kAllOut, IXYWH(24, 24, 27, 27) }, | 
| commit-bot@chromium.org | d3e5842 | 2013-11-05 15:03:08 +0000 | [diff] [blame] | 1141 | }; | 
|  | 1142 |  | 
|  | 1143 | #undef XYWH | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 1144 | #undef IXYWH | 
| commit-bot@chromium.org | d3e5842 | 2013-11-05 15:03:08 +0000 | [diff] [blame] | 1145 |  | 
|  | 1146 | for (size_t i = 0; i < SK_ARRAY_COUNT(testCases); ++i) { | 
| csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame^] | 1147 | const GrReducedClip reduced(stack, testCases[i].testBounds); | 
|  | 1148 | REPORTER_ASSERT(reporter, reduced.elements().count() == testCases[i].reducedClipCount); | 
|  | 1149 | SkASSERT(reduced.elements().count() == testCases[i].reducedClipCount); | 
|  | 1150 | REPORTER_ASSERT(reporter, reduced.genID() == testCases[i].reducedGenID); | 
|  | 1151 | SkASSERT(reduced.genID() == testCases[i].reducedGenID); | 
|  | 1152 | REPORTER_ASSERT(reporter, reduced.initialState() == testCases[i].initialState); | 
|  | 1153 | SkASSERT(reduced.initialState() == testCases[i].initialState); | 
|  | 1154 | REPORTER_ASSERT(reporter, reduced.iBounds() == testCases[i].clipIRect); | 
|  | 1155 | SkASSERT(reduced.iBounds() == testCases[i].clipIRect); | 
| commit-bot@chromium.org | d3e5842 | 2013-11-05 15:03:08 +0000 | [diff] [blame] | 1156 | } | 
|  | 1157 | } | 
|  | 1158 | } | 
|  | 1159 |  | 
|  | 1160 | static void test_reduced_clip_stack_no_aa_crash(skiatest::Reporter* reporter) { | 
|  | 1161 | SkClipStack stack; | 
|  | 1162 | stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 100, 100), SkRegion::kReplace_Op); | 
|  | 1163 | stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 50, 50), SkRegion::kReplace_Op); | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 1164 | SkRect bounds = SkRect::MakeXYWH(0, 0, 100, 100); | 
| commit-bot@chromium.org | d3e5842 | 2013-11-05 15:03:08 +0000 | [diff] [blame] | 1165 |  | 
| commit-bot@chromium.org | d3e5842 | 2013-11-05 15:03:08 +0000 | [diff] [blame] | 1166 | // At the time, this would crash. | 
| csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame^] | 1167 | const GrReducedClip reduced(stack, bounds); | 
|  | 1168 | REPORTER_ASSERT(reporter, reduced.elements().isEmpty()); | 
| commit-bot@chromium.org | d3e5842 | 2013-11-05 15:03:08 +0000 | [diff] [blame] | 1169 | } | 
|  | 1170 |  | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 1171 | enum class ClipMethod { | 
|  | 1172 | kSkipDraw, | 
|  | 1173 | kIgnoreClip, | 
|  | 1174 | kScissor, | 
|  | 1175 | kAAElements | 
|  | 1176 | }; | 
|  | 1177 |  | 
|  | 1178 | static void test_aa_query(skiatest::Reporter* reporter, const SkString& testName, | 
|  | 1179 | const SkClipStack& stack, const SkMatrix& queryXform, | 
|  | 1180 | const SkRect& preXformQuery, ClipMethod expectedMethod, | 
|  | 1181 | int numExpectedElems = 0) { | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 1182 | SkRect queryBounds; | 
|  | 1183 | queryXform.mapRect(&queryBounds, preXformQuery); | 
| csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame^] | 1184 | const GrReducedClip reduced(stack, queryBounds); | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 1185 |  | 
|  | 1186 | SkClipStack::BoundsType stackBoundsType; | 
|  | 1187 | SkRect stackBounds; | 
|  | 1188 | stack.getBounds(&stackBounds, &stackBoundsType); | 
|  | 1189 |  | 
|  | 1190 | switch (expectedMethod) { | 
|  | 1191 | case ClipMethod::kSkipDraw: | 
|  | 1192 | SkASSERT(0 == numExpectedElems); | 
| csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame^] | 1193 | REPORTER_ASSERT_MESSAGE(reporter, reduced.elements().isEmpty(), testName.c_str()); | 
|  | 1194 | REPORTER_ASSERT_MESSAGE(reporter, | 
|  | 1195 | GrReducedClip::InitialState::kAllOut == reduced.initialState(), | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 1196 | testName.c_str()); | 
|  | 1197 | return; | 
|  | 1198 | case ClipMethod::kIgnoreClip: | 
|  | 1199 | SkASSERT(0 == numExpectedElems); | 
| csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame^] | 1200 | REPORTER_ASSERT_MESSAGE(reporter, GrClip::IsInsideClip(reduced.iBounds(), queryBounds), | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 1201 | testName.c_str()); | 
| csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame^] | 1202 | REPORTER_ASSERT_MESSAGE(reporter, reduced.elements().isEmpty(), testName.c_str()); | 
|  | 1203 | REPORTER_ASSERT_MESSAGE(reporter, | 
|  | 1204 | GrReducedClip::InitialState::kAllIn == reduced.initialState(), | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 1205 | testName.c_str()); | 
|  | 1206 | return; | 
|  | 1207 | case ClipMethod::kScissor: { | 
|  | 1208 | SkASSERT(SkClipStack::kNormal_BoundsType == stackBoundsType); | 
|  | 1209 | SkASSERT(0 == numExpectedElems); | 
|  | 1210 | SkIRect expectedScissor; | 
|  | 1211 | stackBounds.round(&expectedScissor); | 
| csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame^] | 1212 | REPORTER_ASSERT_MESSAGE(reporter, reduced.elements().isEmpty(), testName.c_str()); | 
|  | 1213 | REPORTER_ASSERT_MESSAGE(reporter, expectedScissor == reduced.iBounds(), | 
|  | 1214 | testName.c_str()); | 
|  | 1215 | REPORTER_ASSERT_MESSAGE(reporter, | 
|  | 1216 | GrReducedClip::InitialState::kAllIn == reduced.initialState(), | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 1217 | testName.c_str()); | 
|  | 1218 | return; | 
|  | 1219 | } | 
|  | 1220 | case ClipMethod::kAAElements: { | 
|  | 1221 | SkIRect expectedClipIBounds = GrClip::GetPixelIBounds(queryBounds); | 
|  | 1222 | if (SkClipStack::kNormal_BoundsType == stackBoundsType) { | 
|  | 1223 | SkAssertResult(expectedClipIBounds.intersect(GrClip::GetPixelIBounds(stackBounds))); | 
|  | 1224 | } | 
| csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame^] | 1225 | REPORTER_ASSERT_MESSAGE(reporter, numExpectedElems == reduced.elements().count(), | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 1226 | testName.c_str()); | 
| csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame^] | 1227 | REPORTER_ASSERT_MESSAGE(reporter, expectedClipIBounds == reduced.iBounds(), | 
|  | 1228 | testName.c_str()); | 
|  | 1229 | REPORTER_ASSERT_MESSAGE(reporter, reduced.requiresAA() == !reduced.elements().isEmpty(), | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 1230 | testName.c_str()); | 
|  | 1231 | break; | 
|  | 1232 | } | 
|  | 1233 | } | 
|  | 1234 | } | 
|  | 1235 |  | 
|  | 1236 | static void test_reduced_clip_stack_aa(skiatest::Reporter* reporter) { | 
|  | 1237 | constexpr SkScalar IL = 2, IT = 1, IR = 6, IB = 7;         // Pixel aligned rect. | 
|  | 1238 | constexpr SkScalar L = 2.2f, T = 1.7f, R = 5.8f, B = 7.3f; // Generic rect. | 
|  | 1239 | constexpr SkScalar l = 3.3f, t = 2.8f, r = 4.7f, b = 6.2f; // Small rect contained in R. | 
|  | 1240 |  | 
|  | 1241 | SkRect alignedRect = {IL, IT, IR, IB}; | 
|  | 1242 | SkRect rect = {L, T, R, B}; | 
|  | 1243 | SkRect innerRect = {l, t, r, b}; | 
|  | 1244 |  | 
|  | 1245 | SkMatrix m; | 
|  | 1246 | m.setIdentity(); | 
|  | 1247 |  | 
|  | 1248 | constexpr SkScalar kMinScale = 2.0001f; | 
|  | 1249 | constexpr SkScalar kMaxScale = 3; | 
|  | 1250 | constexpr int kNumIters = 8; | 
|  | 1251 |  | 
|  | 1252 | SkString name; | 
|  | 1253 | SkRandom rand; | 
|  | 1254 |  | 
|  | 1255 | for (int i = 0; i < kNumIters; ++i) { | 
|  | 1256 | // Pixel-aligned rect (iior=true). | 
|  | 1257 | name.printf("Pixel-aligned rect test, iter %i", i); | 
|  | 1258 | SkClipStack stack; | 
|  | 1259 | stack.clipDevRect(alignedRect, SkRegion::kIntersect_Op, true); | 
|  | 1260 | test_aa_query(reporter, name, stack, m, {IL, IT, IR, IB}, ClipMethod::kIgnoreClip); | 
|  | 1261 | test_aa_query(reporter, name, stack, m, {IL, IT-1, IR, IT}, ClipMethod::kSkipDraw); | 
| csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame^] | 1262 | test_aa_query(reporter, name, stack, m, {IL, IT-2, IR, IB}, ClipMethod::kScissor); | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 1263 |  | 
|  | 1264 | // Rect (iior=true). | 
|  | 1265 | name.printf("Rect test, iter %i", i); | 
|  | 1266 | stack.reset(); | 
|  | 1267 | stack.clipDevRect(rect, SkRegion::kIntersect_Op, true); | 
|  | 1268 | test_aa_query(reporter, name, stack, m, {L, T,  R, B}, ClipMethod::kIgnoreClip); | 
|  | 1269 | test_aa_query(reporter, name, stack, m, {L-.1f, T, L, B}, ClipMethod::kSkipDraw); | 
|  | 1270 | test_aa_query(reporter, name, stack, m, {L-.1f, T, L+.1f, B}, ClipMethod::kAAElements, 1); | 
|  | 1271 |  | 
|  | 1272 | // Difference rect (iior=false, inside-out bounds). | 
|  | 1273 | name.printf("Difference rect test, iter %i", i); | 
|  | 1274 | stack.reset(); | 
|  | 1275 | stack.clipDevRect(rect, SkRegion::kDifference_Op, true); | 
|  | 1276 | test_aa_query(reporter, name, stack, m, {L, T, R, B}, ClipMethod::kSkipDraw); | 
|  | 1277 | test_aa_query(reporter, name, stack, m, {L, T-.1f, R, T}, ClipMethod::kIgnoreClip); | 
|  | 1278 | test_aa_query(reporter, name, stack, m, {L, T-.1f, R, T+.1f}, ClipMethod::kAAElements, 1); | 
|  | 1279 |  | 
|  | 1280 | // Complex clip (iior=false, normal bounds). | 
|  | 1281 | name.printf("Complex clip test, iter %i", i); | 
|  | 1282 | stack.reset(); | 
|  | 1283 | stack.clipDevRect(rect, SkRegion::kIntersect_Op, true); | 
|  | 1284 | stack.clipDevRect(innerRect, SkRegion::kXOR_Op, true); | 
|  | 1285 | test_aa_query(reporter, name, stack, m, {l, t, r, b}, ClipMethod::kSkipDraw); | 
|  | 1286 | test_aa_query(reporter, name, stack, m, {r-.1f, t, R, b}, ClipMethod::kAAElements, 1); | 
|  | 1287 | test_aa_query(reporter, name, stack, m, {r-.1f, t, R+.1f, b}, ClipMethod::kAAElements, 2); | 
|  | 1288 | test_aa_query(reporter, name, stack, m, {r, t, R+.1f, b}, ClipMethod::kAAElements, 1); | 
|  | 1289 | test_aa_query(reporter, name, stack, m, {r, t, R, b}, ClipMethod::kIgnoreClip); | 
|  | 1290 | test_aa_query(reporter, name, stack, m, {R, T, R+.1f, B}, ClipMethod::kSkipDraw); | 
|  | 1291 |  | 
|  | 1292 | // Complex clip where outer rect is pixel aligned (iior=false, normal bounds). | 
|  | 1293 | name.printf("Aligned Complex clip test, iter %i", i); | 
|  | 1294 | stack.reset(); | 
|  | 1295 | stack.clipDevRect(alignedRect, SkRegion::kIntersect_Op, true); | 
|  | 1296 | stack.clipDevRect(innerRect, SkRegion::kXOR_Op, true); | 
|  | 1297 | test_aa_query(reporter, name, stack, m, {l, t, r, b}, ClipMethod::kSkipDraw); | 
|  | 1298 | test_aa_query(reporter, name, stack, m, {l, b-.1f, r, IB}, ClipMethod::kAAElements, 1); | 
|  | 1299 | test_aa_query(reporter, name, stack, m, {l, b-.1f, r, IB+.1f}, ClipMethod::kAAElements, 1); | 
|  | 1300 | test_aa_query(reporter, name, stack, m, {l, b, r, IB+.1f}, ClipMethod::kAAElements, 0); | 
|  | 1301 | test_aa_query(reporter, name, stack, m, {l, b, r, IB}, ClipMethod::kIgnoreClip); | 
|  | 1302 | test_aa_query(reporter, name, stack, m, {IL, IB, IR, IB+.1f}, ClipMethod::kSkipDraw); | 
|  | 1303 |  | 
|  | 1304 | // Apply random transforms and try again. This ensures the clip stack reduction is hardened | 
|  | 1305 | // against FP rounding error. | 
|  | 1306 | SkScalar sx = rand.nextRangeScalar(kMinScale, kMaxScale); | 
|  | 1307 | sx = SkScalarFloorToScalar(sx * alignedRect.width()) / alignedRect.width(); | 
|  | 1308 | SkScalar sy = rand.nextRangeScalar(kMinScale, kMaxScale); | 
|  | 1309 | sy = SkScalarFloorToScalar(sy * alignedRect.height()) / alignedRect.height(); | 
|  | 1310 | SkScalar tx = SkScalarRoundToScalar(sx * alignedRect.x()) - sx * alignedRect.x(); | 
|  | 1311 | SkScalar ty = SkScalarRoundToScalar(sy * alignedRect.y()) - sy * alignedRect.y(); | 
|  | 1312 |  | 
|  | 1313 | SkMatrix xform = SkMatrix::MakeScale(sx, sy); | 
|  | 1314 | xform.postTranslate(tx, ty); | 
|  | 1315 | xform.mapRect(&alignedRect); | 
|  | 1316 | xform.mapRect(&rect); | 
|  | 1317 | xform.mapRect(&innerRect); | 
|  | 1318 | m.postConcat(xform); | 
|  | 1319 | } | 
|  | 1320 | } | 
|  | 1321 |  | 
| bsalomon@google.com | a4e13c8 | 2012-11-26 21:38:37 +0000 | [diff] [blame] | 1322 | #endif | 
| bsalomon@google.com | 51a6286 | 2012-11-26 21:19:43 +0000 | [diff] [blame] | 1323 |  | 
| tfarina@chromium.org | e4fafb1 | 2013-12-12 21:11:12 +0000 | [diff] [blame] | 1324 | DEF_TEST(ClipStack, reporter) { | 
| reed@google.com | bdee9fc | 2011-02-22 20:17:43 +0000 | [diff] [blame] | 1325 | SkClipStack stack; | 
|  | 1326 |  | 
| robertphillips@google.com | 80214e2 | 2012-07-20 15:33:18 +0000 | [diff] [blame] | 1327 | REPORTER_ASSERT(reporter, 0 == stack.getSaveCount()); | 
| reed@google.com | bdee9fc | 2011-02-22 20:17:43 +0000 | [diff] [blame] | 1328 | assert_count(reporter, stack, 0); | 
|  | 1329 |  | 
|  | 1330 | static const SkIRect gRects[] = { | 
|  | 1331 | { 0, 0, 100, 100 }, | 
|  | 1332 | { 25, 25, 125, 125 }, | 
|  | 1333 | { 0, 0, 1000, 1000 }, | 
|  | 1334 | { 0, 0, 75, 75 } | 
|  | 1335 | }; | 
|  | 1336 | for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) { | 
| reed@google.com | d9f2dea | 2011-10-12 14:43:27 +0000 | [diff] [blame] | 1337 | stack.clipDevRect(gRects[i], SkRegion::kIntersect_Op); | 
| reed@google.com | bdee9fc | 2011-02-22 20:17:43 +0000 | [diff] [blame] | 1338 | } | 
|  | 1339 |  | 
|  | 1340 | // all of the above rects should have been intersected, leaving only 1 rect | 
| robertphillips@google.com | 80214e2 | 2012-07-20 15:33:18 +0000 | [diff] [blame] | 1341 | SkClipStack::B2TIter iter(stack); | 
| bsalomon@google.com | 8182fa0 | 2012-12-04 14:06:06 +0000 | [diff] [blame] | 1342 | const SkClipStack::Element* element = iter.next(); | 
| epoger@google.com | 2047f00 | 2011-05-17 17:36:59 +0000 | [diff] [blame] | 1343 | SkRect answer; | 
|  | 1344 | answer.iset(25, 25, 75, 75); | 
| reed@google.com | bdee9fc | 2011-02-22 20:17:43 +0000 | [diff] [blame] | 1345 |  | 
| bsalomon | 49f085d | 2014-09-05 13:34:00 -0700 | [diff] [blame] | 1346 | REPORTER_ASSERT(reporter, element); | 
| bsalomon@google.com | 8182fa0 | 2012-12-04 14:06:06 +0000 | [diff] [blame] | 1347 | REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType()); | 
|  | 1348 | REPORTER_ASSERT(reporter, SkRegion::kIntersect_Op == element->getOp()); | 
|  | 1349 | REPORTER_ASSERT(reporter, element->getRect() == answer); | 
| reed@google.com | bdee9fc | 2011-02-22 20:17:43 +0000 | [diff] [blame] | 1350 | // now check that we only had one in our iterator | 
|  | 1351 | REPORTER_ASSERT(reporter, !iter.next()); | 
|  | 1352 |  | 
|  | 1353 | stack.reset(); | 
| robertphillips@google.com | 80214e2 | 2012-07-20 15:33:18 +0000 | [diff] [blame] | 1354 | REPORTER_ASSERT(reporter, 0 == stack.getSaveCount()); | 
| reed@google.com | bdee9fc | 2011-02-22 20:17:43 +0000 | [diff] [blame] | 1355 | assert_count(reporter, stack, 0); | 
| vandebo@chromium.org | 1e1c36f | 2011-05-03 16:26:09 +0000 | [diff] [blame] | 1356 |  | 
|  | 1357 | test_assign_and_comparison(reporter); | 
| robertphillips@google.com | 80214e2 | 2012-07-20 15:33:18 +0000 | [diff] [blame] | 1358 | test_iterators(reporter); | 
| commit-bot@chromium.org | e5b2af9 | 2014-02-16 13:25:24 +0000 | [diff] [blame] | 1359 | test_bounds(reporter, SkClipStack::Element::kRect_Type); | 
|  | 1360 | test_bounds(reporter, SkClipStack::Element::kRRect_Type); | 
|  | 1361 | test_bounds(reporter, SkClipStack::Element::kPath_Type); | 
| robertphillips@google.com | cc6493b | 2012-07-26 18:39:13 +0000 | [diff] [blame] | 1362 | test_isWideOpen(reporter); | 
| robertphillips@google.com | 08eacc1 | 2012-08-02 12:49:00 +0000 | [diff] [blame] | 1363 | test_rect_merging(reporter); | 
| commit-bot@chromium.org | 6fbe54c | 2013-06-11 11:01:48 +0000 | [diff] [blame] | 1364 | test_rect_replace(reporter); | 
| junov@chromium.org | edf32d5 | 2012-12-10 14:57:54 +0000 | [diff] [blame] | 1365 | test_rect_inverse_fill(reporter); | 
| commit-bot@chromium.org | 6fbe54c | 2013-06-11 11:01:48 +0000 | [diff] [blame] | 1366 | test_path_replace(reporter); | 
| junov@chromium.org | 8cdf0f5 | 2012-12-12 17:58:15 +0000 | [diff] [blame] | 1367 | test_quickContains(reporter); | 
| csmartdalton | d50e240 | 2016-07-22 08:39:06 -0700 | [diff] [blame] | 1368 | test_invfill_diff_bug(reporter); | 
| bsalomon@google.com | e7b3d29 | 2012-11-26 21:42:32 +0000 | [diff] [blame] | 1369 | #if SK_SUPPORT_GPU | 
| bsalomon@google.com | edb26fd | 2012-11-28 14:42:41 +0000 | [diff] [blame] | 1370 | test_reduced_clip_stack(reporter); | 
| commit-bot@chromium.org | d3e5842 | 2013-11-05 15:03:08 +0000 | [diff] [blame] | 1371 | test_reduced_clip_stack_genid(reporter); | 
|  | 1372 | test_reduced_clip_stack_no_aa_crash(reporter); | 
| csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 1373 | test_reduced_clip_stack_aa(reporter); | 
| bsalomon@google.com | e7b3d29 | 2012-11-26 21:42:32 +0000 | [diff] [blame] | 1374 | #endif | 
| reed@google.com | bdee9fc | 2011-02-22 20:17:43 +0000 | [diff] [blame] | 1375 | } |