Flutter Engine
The Flutter Engine
Functions
GrClipStackTest.cpp File Reference
#include "include/core/SkClipOp.h"
#include "include/core/SkColorSpace.h"
#include "include/core/SkMatrix.h"
#include "include/core/SkPath.h"
#include "include/core/SkPathTypes.h"
#include "include/core/SkPoint.h"
#include "include/core/SkRRect.h"
#include "include/core/SkRect.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkRegion.h"
#include "include/core/SkScalar.h"
#include "include/core/SkShader.h"
#include "include/core/SkString.h"
#include "include/core/SkSurfaceProps.h"
#include "include/core/SkTypes.h"
#include "include/gpu/GrContextOptions.h"
#include "include/gpu/GrDirectContext.h"
#include "include/gpu/mock/GrMockTypes.h"
#include "include/private/base/SkTo.h"
#include "include/private/gpu/ganesh/GrTypesPriv.h"
#include "src/core/SkRRectPriv.h"
#include "src/gpu/ResourceKey.h"
#include "src/gpu/SkBackingFit.h"
#include "src/gpu/ganesh/ClipStack.h"
#include "src/gpu/ganesh/GrAppliedClip.h"
#include "src/gpu/ganesh/GrClip.h"
#include "src/gpu/ganesh/GrDirectContextPriv.h"
#include "src/gpu/ganesh/GrPaint.h"
#include "src/gpu/ganesh/GrProcessorSet.h"
#include "src/gpu/ganesh/GrProxyProvider.h"
#include "src/gpu/ganesh/GrResourceCache.h"
#include "src/gpu/ganesh/GrScissorState.h"
#include "src/gpu/ganesh/GrWindowRectsState.h"
#include "src/gpu/ganesh/SurfaceDrawContext.h"
#include "src/gpu/ganesh/geometry/GrShape.h"
#include "src/gpu/ganesh/ops/GrDrawOp.h"
#include "src/gpu/ganesh/ops/GrOp.h"
#include "tests/CtsEnforcement.h"
#include "tests/Test.h"
#include <cstddef>
#include <initializer_list>
#include <memory>
#include <tuple>
#include <utility>
#include <vector>

Go to the source code of this file.

Functions

 DEF_TEST (ClipStack_InitialState, r)
 
 DEF_TEST (ClipStack_RectRectAACombine, r)
 
 DEF_TEST (ClipStack_DifferenceNoCombine, r)
 
 DEF_TEST (ClipStack_RectRectNonAxisAligned, r)
 
 DEF_TEST (ClipStack_RRectRRectAACombine, r)
 
 DEF_TEST (ClipStack_RectRRectCombine, r)
 
 DEF_TEST (ClipStack_RectDeviceClip, r)
 
 DEF_TEST (ClipStack_ShapeDeviceBoundsClip, r)
 
 DEF_TEST (ClipStack_PathSimplify, r)
 
 DEF_TEST (ClipStack_RepeatElement, r)
 
 DEF_TEST (ClipStack_InverseFilledPath, r)
 
 DEF_TEST (ClipStack_Offscreen, r)
 
 DEF_TEST (ClipStack_EmptyShape, r)
 
 DEF_TEST (ClipStack_DifferenceBounds, r)
 
 DEF_TEST (ClipStack_NoDifferenceInterference, r)
 
 DEF_TEST (ClipStack_MultiplePaths, r)
 
 DEF_TEST (ClipStack_DeviceRect, r)
 
 DEF_TEST (ClipStack_DeviceRRect, r)
 
 DEF_TEST (ClipStack_ScaleTranslate, r)
 
 DEF_TEST (ClipStack_PreserveAxisAlignment, r)
 
 DEF_TEST (ClipStack_ConvexPathContains, r)
 
 DEF_TEST (ClipStack_NonAxisAlignedContains, r)
 
 DEF_TEST (ClipStack_MixedAAContains, r)
 
 DEF_TEST (ClipStack_ShapeContainsDevice, r)
 
 DEF_TEST (ClipStack_DisjointShapes, r)
 
 DEF_TEST (ClipStack_ComplexClip, reporter)
 
 DEF_TEST (ClipStack_ReplaceClip, r)
 
 DEF_TEST (ClipStack_DiffRects, r)
 
 DEF_TEST (ClipStack_ForceAA, r)
 
 DEF_TEST (ClipStack_PreApply, r)
 
 DEF_TEST (ClipStack_Shader, r)
 
 DEF_TEST (ClipStack_SimpleApply, r)
 
static void disable_tessellation_atlas (GrContextOptions *options)
 
 DEF_GANESH_TEST_FOR_CONTEXTS (ClipStack_SWMask, skgpu::IsRenderingContext, r, ctxInfo, disable_tessellation_atlas, CtsEnforcement::kNever)
 

Function Documentation

◆ DEF_GANESH_TEST_FOR_CONTEXTS()

DEF_GANESH_TEST_FOR_CONTEXTS ( ClipStack_SWMask  ,
skgpu::IsRenderingContext  ,
,
ctxInfo  ,
disable_tessellation_atlas  ,
CtsEnforcement::kNever   
)

Definition at line 2093 of file GrClipStackTest.cpp.

2098 {
2099 using ClipStack = skgpu::ganesh::ClipStack;
2101
2102 GrDirectContext* context = ctxInfo.directContext();
2103 std::unique_ptr<SurfaceDrawContext> sdc = SurfaceDrawContext::Make(
2104 context, GrColorType::kRGBA_8888, nullptr, SkBackingFit::kExact, kDeviceBounds.size(),
2105 SkSurfaceProps(), /*label=*/{});
2106
2107 std::unique_ptr<ClipStack> cs(new ClipStack(kDeviceBounds, &SkMatrix::I(), false));
2108
2109 auto addMaskRequiringClip = [&](SkScalar x, SkScalar y, SkScalar radius) {
2110 SkPath path;
2111 path.addCircle(x, y, radius);
2112 path.addCircle(x + radius / 2.f, y + radius / 2.f, radius);
2113 path.setFillType(SkPathFillType::kEvenOdd);
2114
2115 // Use AA so that clip application does not route through the stencil buffer
2117 };
2118
2119 auto drawRect = [&](SkRect drawBounds) {
2120 GrPaint paint;
2121 paint.setColor4f({1.f, 1.f, 1.f, 1.f});
2122 sdc->drawRect(cs.get(), std::move(paint), GrAA::kYes, SkMatrix::I(), drawBounds);
2123 };
2124
2125 auto generateMask = [&](SkRect drawBounds) {
2126 skgpu::UniqueKey priorKey = cs->testingOnly_getLastSWMaskKey();
2127 drawRect(drawBounds);
2128 skgpu::UniqueKey newKey = cs->testingOnly_getLastSWMaskKey();
2129 REPORTER_ASSERT(r, priorKey != newKey, "Did not generate a new SW mask key as expected");
2130 return newKey;
2131 };
2132
2133 auto verifyKeys = [&](const std::vector<skgpu::UniqueKey>& expectedKeys,
2134 const std::vector<skgpu::UniqueKey>& releasedKeys) {
2135 context->flush();
2136 GrProxyProvider* proxyProvider = context->priv().proxyProvider();
2137
2138#ifdef SK_DEBUG
2139 // The proxy providers key count fluctuates based on proxy lifetime, but we want to
2140 // verify the resource count, and that requires using key tags that are debug-only.
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);
2145 REPORTER_ASSERT(r, (int) expectedKeys.size() == numProxies,
2146 "Unexpected proxy count, got %d, not %d",
2147 numProxies, (int) expectedKeys.size());
2148#endif
2149
2150 for (const auto& key : expectedKeys) {
2151 auto proxy = proxyProvider->findOrCreateProxyByUniqueKey(key);
2152 REPORTER_ASSERT(r, SkToBool(proxy), "Unable to find resource for expected mask key");
2153 }
2154 for (const auto& key : releasedKeys) {
2155 auto proxy = proxyProvider->findOrCreateProxyByUniqueKey(key);
2156 REPORTER_ASSERT(r, !SkToBool(proxy), "SW mask not released as expected");
2157 }
2158 };
2159
2160 // Creates a mask for a complex clip
2161 cs->save();
2162 addMaskRequiringClip(5.f, 5.f, 20.f);
2163 skgpu::UniqueKey keyADepth1 = generateMask({0.f, 0.f, 20.f, 20.f});
2164 skgpu::UniqueKey keyBDepth1 = generateMask({10.f, 10.f, 30.f, 30.f});
2165 verifyKeys({keyADepth1, keyBDepth1}, {});
2166
2167 // Creates a new mask for a new save record, but doesn't delete the old records
2168 cs->save();
2169 addMaskRequiringClip(6.f, 6.f, 15.f);
2170 skgpu::UniqueKey keyADepth2 = generateMask({0.f, 0.f, 20.f, 20.f});
2171 skgpu::UniqueKey keyBDepth2 = generateMask({10.f, 10.f, 30.f, 30.f});
2172 verifyKeys({keyADepth1, keyBDepth1, keyADepth2, keyBDepth2}, {});
2173
2174 // Release after modifying the current record (even if we don't draw anything)
2175 addMaskRequiringClip(4.f, 4.f, 15.f);
2176 skgpu::UniqueKey keyCDepth2 = generateMask({4.f, 4.f, 16.f, 20.f});
2177 verifyKeys({keyADepth1, keyBDepth1, keyCDepth2}, {keyADepth2, keyBDepth2});
2178
2179 // Release after restoring an older record
2180 cs->restore();
2181 verifyKeys({keyADepth1, keyBDepth1}, {keyCDepth2});
2182
2183 // Drawing finds the old masks at depth 1 still w/o making new ones
2184 drawRect({0.f, 0.f, 20.f, 20.f});
2185 drawRect({10.f, 10.f, 30.f, 30.f});
2186 verifyKeys({keyADepth1, keyBDepth1}, {});
2187
2188 // Drawing something contained within a previous mask also does not make a new one
2189 drawRect({5.f, 5.f, 15.f, 15.f});
2190 verifyKeys({keyADepth1, keyBDepth1}, {});
2191
2192 // Release on destruction
2193 cs = nullptr;
2194 verifyKeys({}, {keyADepth1, keyBDepth1});
2195}
skgpu::ganesh::SurfaceDrawContext SurfaceDrawContext
Definition: ClearTest.cpp:46
#define SkASSERT(cond)
Definition: SkAssert.h:116
static constexpr bool SkToBool(const T &x)
Definition: SkTo.h:35
#define REPORTER_ASSERT(r, cond,...)
Definition: Test.h:286
GrResourceCache * getResourceCache()
GrSemaphoresSubmitted flush(const GrFlushInfo &info)
GrDirectContextPriv priv()
sk_sp< GrTextureProxy > findOrCreateProxyByUniqueKey(const skgpu::UniqueKey &, UseAllocator=UseAllocator::kYes)
GrProxyProvider * proxyProvider()
static const SkMatrix & I()
Definition: SkMatrix.cpp:1544
Definition: SkPath.h:59
static std::unique_ptr< SurfaceDrawContext > Make(GrRecordingContext *, GrColorType, sk_sp< GrSurfaceProxy >, sk_sp< SkColorSpace >, GrSurfaceOrigin, const SkSurfaceProps &)
const Paint & paint
Definition: color_source.cc:38
float SkScalar
Definition: extension.cpp:12
double y
double x
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 to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets Path to the Flutter assets directory enable service port Allow the VM service to fallback to automatic port selection if binding to a specified port fails trace Trace early application lifecycle Automatically switches to an endless trace buffer trace skia Filters out all Skia trace event categories except those that are specified in this comma separated list dump skp on shader Automatically dump the skp that triggers new shader compilations This is useful for writing custom ShaderWarmUp to reduce jank By this is not enabled to reduce the overhead purge persistent cache
Definition: switches.h:191
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
Definition: switches.h:57

◆ DEF_TEST() [1/32]

DEF_TEST ( ClipStack_ComplexClip  ,
reporter   
)

Definition at line 1618 of file GrClipStackTest.cpp.

1618 {
1619 using ClipStack = skgpu::ganesh::ClipStack;
1620
1621 static constexpr float kN = 10.f;
1622 static constexpr float kR = kN / 3.f;
1623
1624 // 4 rectangles that overlap by kN x 2kN (horiz), 2kN x kN (vert), or kN x kN (diagonal)
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};
1629
1630 enum ShapeType { kRect, kRRect, kConvex };
1631
1632 SkRect rects[] = { kTL, kTR, kBL, kBR };
1633 for (ShapeType type : { kRect, kRRect, kConvex }) {
1634 for (int opBits = 6; opBits < 16; ++opBits) {
1635 SkString name;
1636 name.appendf("complex-%d-%d", (int) type, opBits);
1637
1638 SkRect expectedRectIntersection = SkRect::Make(kDeviceBounds);
1639 SkRRect expectedRRectIntersection = SkRRect::MakeRect(expectedRectIntersection);
1640
1641 auto b = TestCase::Build(name.c_str(), kDeviceBounds);
1642 for (int i = 0; i < 4; ++i) {
1643 SkClipOp op = (opBits & (1 << i)) ? SkClipOp::kIntersect : SkClipOp::kDifference;
1644 switch(type) {
1645 case kRect: {
1646 SkRect r = rects[i];
1647 if (op == SkClipOp::kDifference) {
1648 // Shrink the rect for difference ops, otherwise in the rect testcase
1649 // any difference op would remove the intersection of the other ops
1650 // given how the rects are defined, and that's just not interesting.
1651 r.inset(kR, kR);
1652 }
1653 b.actual().rect(r, GrAA::kYes, op);
1654 if (op == SkClipOp::kIntersect) {
1655 SkAssertResult(expectedRectIntersection.intersect(r));
1656 } else {
1657 b.expect().rect(r, GrAA::kYes, SkClipOp::kDifference);
1658 }
1659 break; }
1660 case kRRect: {
1661 SkRRect rrect = SkRRect::MakeRectXY(rects[i], kR, kR);
1662 b.actual().rrect(rrect, GrAA::kYes, op);
1663 if (op == SkClipOp::kIntersect) {
1664 expectedRRectIntersection = SkRRectPriv::ConservativeIntersect(
1665 expectedRRectIntersection, rrect);
1666 SkASSERT(!expectedRRectIntersection.isEmpty());
1667 } else {
1668 b.expect().rrect(rrect, GrAA::kYes, SkClipOp::kDifference);
1669 }
1670 break; }
1671 case kConvex:
1672 b.actual().path(make_octagon(rects[i], kR, kR), GrAA::kYes, op);
1673 // NOTE: We don't set any expectations here, since convex just calls
1674 // expectActual() at the end.
1675 break;
1676 }
1677 }
1678
1679 // The expectations differ depending on the shape type
1680 ClipStack::ClipState state = ClipStack::ClipState::kComplex;
1681 if (type == kConvex) {
1682 // The simplest case is when the paths cannot be combined together, so we expect
1683 // the actual elements to be unmodified (both intersect and difference).
1684 b.expectActual();
1685 } else if (opBits) {
1686 // All intersection ops were pre-computed into expectedR[R]ectIntersection
1687 // - difference ops already added in the for loop
1688 if (type == kRect) {
1689 SkASSERT(expectedRectIntersection != SkRect::Make(kDeviceBounds) &&
1690 !expectedRectIntersection.isEmpty());
1691 b.expect().rect(expectedRectIntersection, GrAA::kYes, SkClipOp::kIntersect);
1692 if (opBits == 0xf) {
1694 }
1695 } else {
1696 SkASSERT(expectedRRectIntersection !=
1697 SkRRect::MakeRect(SkRect::Make(kDeviceBounds)) &&
1698 !expectedRRectIntersection.isEmpty());
1699 b.expect().rrect(expectedRRectIntersection, GrAA::kYes, SkClipOp::kIntersect);
1700 if (opBits == 0xf) {
1701 state = ClipStack::ClipState::kDeviceRRect;
1702 }
1703 }
1704 }
1705
1706 run_test_case(reporter, b.state(state).finishTest());
1707 }
1708 }
1709}
reporter
Definition: FontMgrTest.cpp:39
SkAssertResult(font.textToGlyphs("Hello", 5, SkTextEncoding::kUTF8, glyphs, std::size(glyphs))==count)
static const uint16_t kTL
static const uint16_t kBL
static const uint16_t kBR
static const uint16_t kTR
SkClipOp
Definition: SkClipOp.h:13
@ kRRect
GLenum type
static void run_test_case(const SkString &testdata, const SkBitmap &bitmap, SkCanvas *canvas)
Definition: chrome_fuzz.cpp:31
static SkRRect ConservativeIntersect(const SkRRect &a, const SkRRect &b)
Definition: SkRRect.cpp:812
static SkRRect MakeRect(const SkRect &r)
Definition: SkRRect.h:149
static SkRRect MakeRectXY(const SkRect &rect, SkScalar xRad, SkScalar yRad)
Definition: SkRRect.h:180
bool isEmpty() const
Definition: SkRRect.h:83
static bool b
AtkStateType state
SkRRect rrect
Definition: SkRecords.h:232
def Build(configs, env, options)
Definition: build.py:232
constexpr std::array< std::array< float, 2 >, 2 > kRect
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
static SkRect Make(const SkISize &size)
Definition: SkRect.h:669
void inset(float dx, float dy)
Definition: SkRect.h:1060
bool intersect(const SkRect &r)
Definition: SkRect.cpp:114
bool isEmpty() const
Definition: SkRect.h:693
static constexpr SkIRect kDeviceRect

◆ DEF_TEST() [2/32]

DEF_TEST ( ClipStack_ConvexPathContains  ,
 
)

Definition at line 1285 of file GrClipStackTest.cpp.

1285 {
1286 using ClipState = skgpu::ganesh::ClipStack::ClipState;
1287
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);
1291
1292 // Intersect -> path element isn't kept
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()
1297 .finishTest());
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)
1302 .finishTest());
1303
1304 // Difference -> path element is the only one left
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)
1309 .finishTest());
1310 run_test_case(r, TestCase::Build("convex+rrect-difference", kDeviceBounds)
1311 .actual().aa().difference().rrect(rrect).path(bigPath)
1312 .finishElements()
1313 .expect().aa().difference().path(bigPath).finishElements()
1314 .state(ClipState::kComplex)
1315 .finishTest());
1316
1317 // Intersect small shape + difference big path -> empty
1318 run_test_case(r, TestCase::Build("convex-diff+rect-int", kDeviceBounds)
1319 .actual().aa().intersect().rect(rect)
1320 .difference().path(bigPath).finishElements()
1322 .finishTest());
1323 run_test_case(r, TestCase::Build("convex-diff+rrect-int", kDeviceBounds)
1324 .actual().aa().intersect().rrect(rrect)
1325 .difference().path(bigPath).finishElements()
1327 .finishTest());
1328
1329 // Diff small shape + intersect big path -> both
1330 run_test_case(r, TestCase::Build("convex-int+rect-diff", kDeviceBounds)
1331 .actual().aa().intersect().path(bigPath).difference().rect(rect)
1332 .finishElements()
1333 .expectActual()
1334 .state(ClipState::kComplex)
1335 .finishTest());
1336 run_test_case(r, TestCase::Build("convex-int+rrect-diff", kDeviceBounds)
1337 .actual().aa().intersect().path(bigPath).difference().rrect(rrect)
1338 .finishElements()
1339 .expectActual()
1340 .state(ClipState::kComplex)
1341 .finishTest());
1342}
static bool intersect(const SkPoint &p0, const SkPoint &n0, const SkPoint &p1, const SkPoint &n1, SkScalar *t)
static size_t difference(size_t minuend, size_t subtrahend)
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350

◆ DEF_TEST() [3/32]

DEF_TEST ( ClipStack_DeviceRect  ,
 
)

Definition at line 1145 of file GrClipStackTest.cpp.

1145 {
1146 using ClipState = skgpu::ganesh::ClipStack::ClipState;
1147
1148 // Axis-aligned + intersect -> kDeviceRect
1149 SkRect rect = {0, 0, 20, 20};
1150 run_test_case(r, TestCase::Build("device-rect", kDeviceBounds)
1151 .actual().intersect().aa().rect(rect).finishElements()
1152 .expectActual()
1154 .finishTest());
1155
1156 // Not axis-aligned -> kComplex
1157 SkMatrix lm = SkMatrix::RotateDeg(15.f);
1158 run_test_case(r, TestCase::Build("unaligned-rect", kDeviceBounds)
1159 .actual().localToDevice(lm).intersect().aa().rect(rect)
1160 .finishElements()
1161 .expectActual()
1162 .state(ClipState::kComplex)
1163 .finishTest());
1164
1165 // Not intersect -> kComplex
1166 run_test_case(r, TestCase::Build("diff-rect", kDeviceBounds)
1167 .actual().difference().aa().rect(rect).finishElements()
1168 .expectActual()
1169 .state(ClipState::kComplex)
1170 .finishTest());
1171}
static SkMatrix RotateDeg(SkScalar deg)
Definition: SkMatrix.h:104

◆ DEF_TEST() [4/32]

DEF_TEST ( ClipStack_DeviceRRect  ,
 
)

Definition at line 1174 of file GrClipStackTest.cpp.

1174 {
1175 using ClipState = skgpu::ganesh::ClipStack::ClipState;
1176
1177 // Axis-aligned + intersect -> kDeviceRRect
1178 SkRect rect = {0, 0, 20, 20};
1180 run_test_case(r, TestCase::Build("device-rrect", kDeviceBounds)
1181 .actual().intersect().aa().rrect(rrect).finishElements()
1182 .expectActual()
1183 .state(ClipState::kDeviceRRect)
1184 .finishTest());
1185
1186 // Not axis-aligned -> kComplex
1187 SkMatrix lm = SkMatrix::RotateDeg(15.f);
1188 run_test_case(r, TestCase::Build("unaligned-rrect", kDeviceBounds)
1189 .actual().localToDevice(lm).intersect().aa().rrect(rrect)
1190 .finishElements()
1191 .expectActual()
1192 .state(ClipState::kComplex)
1193 .finishTest());
1194
1195 // Not intersect -> kComplex
1196 run_test_case(r, TestCase::Build("diff-rrect", kDeviceBounds)
1197 .actual().difference().aa().rrect(rrect).finishElements()
1198 .expectActual()
1199 .state(ClipState::kComplex)
1200 .finishTest());
1201}

◆ DEF_TEST() [5/32]

DEF_TEST ( ClipStack_DifferenceBounds  ,
 
)

Definition at line 1072 of file GrClipStackTest.cpp.

1072 {
1073 using ClipState = skgpu::ganesh::ClipStack::ClipState;
1074
1075 SkRect rightSide = {50.f, -10.f, 2.f * kDeviceBounds.fRight, kDeviceBounds.fBottom + 10.f};
1076 SkRect clipped = rightSide;
1077 SkAssertResult(clipped.intersect(SkRect::Make(kDeviceBounds)));
1078
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)
1083 .finishTest());
1084}
SkScalar fRight
larger x-axis bounds
Definition: extension.cpp:16

◆ DEF_TEST() [6/32]

DEF_TEST ( ClipStack_DifferenceNoCombine  ,
 
)

Definition at line 605 of file GrClipStackTest.cpp.

605 {
606 using ClipState = skgpu::ganesh::ClipStack::ClipState;
607
608 SkRect r1 = {15.f, 14.f, 23.22f, 58.2f};
609 SkRect r2 = r1.makeOffset(5.f, 8.f);
610 SkASSERT(r1.intersects(r2));
611
612 run_test_case(r, TestCase::Build("no-combine", kDeviceBounds)
613 .actual().aa().intersect().rect(r1)
614 .difference().rect(r2)
615 .finishElements()
616 .expectActual()
617 .state(ClipState::kComplex)
618 .finishTest());
619}
constexpr SkRect makeOffset(float dx, float dy) const
Definition: SkRect.h:965
bool intersects(const SkRect &r) const
Definition: SkRect.h:1121

◆ DEF_TEST() [7/32]

DEF_TEST ( ClipStack_DiffRects  ,
 
)

Definition at line 1751 of file GrClipStackTest.cpp.

1751 {
1752 using ClipStack = skgpu::ganesh::ClipStack;
1754
1756 options.fMaxWindowRectangles = 8;
1757
1759 std::unique_ptr<SurfaceDrawContext> sdc = SurfaceDrawContext::Make(
1761 SkBackingFit::kExact, kDeviceBounds.size(), SkSurfaceProps(),
1762 /*label=*/{});
1763
1764 ClipStack cs(kDeviceBounds, &SkMatrix::I(), false);
1765
1766 cs.save();
1767 for (int y = 0; y < 10; ++y) {
1768 for (int x = 0; x < 10; ++x) {
1769 cs.clipRect(SkMatrix::I(), SkRect::MakeXYWH(10*x+1, 10*y+1, 8, 8),
1771 }
1772 }
1773
1774 GrAppliedClip out(kDeviceBounds.size());
1775 SkRect drawBounds = SkRect::Make(kDeviceBounds);
1776 GrClip::Effect effect = cs.apply(context.get(), sdc.get(), NoOp::Get(), GrAAType::kCoverage,
1777 &out, &drawBounds);
1778
1780 REPORTER_ASSERT(r, out.windowRectsState().numWindows() == 8);
1781
1782 cs.restore();
1783}
const char * options
Effect
Definition: GrClip.h:31
static sk_sp< GrDirectContext > MakeMock(const GrMockOptions *, const GrContextOptions &)
static sk_sp< SkColorSpace > MakeSRGB()
T * get() const
Definition: SkRefCnt.h:303
const GrXPFactory * Get(SkBlendMode mode)
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition: SkRect.h:659

◆ DEF_TEST() [8/32]

DEF_TEST ( ClipStack_DisjointShapes  ,
 
)

Definition at line 1569 of file GrClipStackTest.cpp.

1569 {
1570 using ClipState = skgpu::ganesh::ClipStack::ClipState;
1571
1572 SkRect rt = {10.f, 10.f, 20.f, 20.f};
1573 SkRRect rr = SkRRect::MakeOval(rt.makeOffset({20.f, 0.f}));
1574 SkPath p = make_octagon(rt.makeOffset({0.f, 20.f}));
1575
1576 // I+I
1577 run_test_case(r, TestCase::Build("iii", kDeviceBounds)
1578 .actual().aa().intersect().rect(rt).rrect(rr).path(p).finishElements()
1580 .finishTest());
1581
1582 // D+D
1583 run_test_case(r, TestCase::Build("ddd", kDeviceBounds)
1584 .actual().nonAA().difference().rect(rt).rrect(rr).path(p)
1585 .finishElements()
1586 .expectActual()
1587 .state(ClipState::kComplex)
1588 .finishTest());
1589
1590 // I+D from rect
1591 run_test_case(r, TestCase::Build("idd", kDeviceBounds)
1592 .actual().aa().intersect().rect(rt)
1593 .nonAA().difference().rrect(rr).path(p)
1594 .finishElements()
1595 .expect().aa().intersect().rect(rt).finishElements()
1597 .finishTest());
1598
1599 // I+D from rrect
1600 run_test_case(r, TestCase::Build("did", kDeviceBounds)
1601 .actual().aa().intersect().rrect(rr)
1602 .nonAA().difference().rect(rt).path(p)
1603 .finishElements()
1604 .expect().aa().intersect().rrect(rr).finishElements()
1605 .state(ClipState::kDeviceRRect)
1606 .finishTest());
1607
1608 // I+D from path
1609 run_test_case(r, TestCase::Build("ddi", kDeviceBounds)
1610 .actual().aa().intersect().path(p)
1611 .nonAA().difference().rect(rt).rrect(rr)
1612 .finishElements()
1613 .expect().aa().intersect().path(p).finishElements()
1614 .state(ClipState::kComplex)
1615 .finishTest());
1616}
static SkRRect MakeOval(const SkRect &oval)
Definition: SkRRect.h:162

◆ DEF_TEST() [9/32]

DEF_TEST ( ClipStack_EmptyShape  ,
 
)

Definition at line 1047 of file GrClipStackTest.cpp.

1047 {
1048 using ClipState = skgpu::ganesh::ClipStack::ClipState;
1049
1050 // Intersect -> empty
1051 run_test_case(r, TestCase::Build("empty-intersect", kDeviceBounds)
1052 .actual().intersect().rect(SkRect::MakeEmpty()).finishElements()
1054 .finishTest());
1055
1056 // Difference -> no-op
1057 run_test_case(r, TestCase::Build("empty-difference", kDeviceBounds)
1058 .actual().difference().rect(SkRect::MakeEmpty()).finishElements()
1059 .state(ClipState::kWideOpen)
1060 .finishTest());
1061
1062 SkRRect rrect = SkRRect::MakeRectXY({4.f, 10.f, 16.f, 32.f}, 2.f, 2.f);
1063 run_test_case(r, TestCase::Build("noop-difference", kDeviceBounds)
1065 .finishElements()
1066 .expect().difference().rrect(rrect).finishElements()
1067 .state(ClipState::kComplex)
1068 .finishTest());
1069}
static constexpr SkRect MakeEmpty()
Definition: SkRect.h:595

◆ DEF_TEST() [10/32]

DEF_TEST ( ClipStack_ForceAA  ,
 
)

Definition at line 1786 of file GrClipStackTest.cpp.

1786 {
1787 using ClipStack = skgpu::ganesh::ClipStack;
1788
1789 ClipStack cs(kDeviceBounds, nullptr, true);
1790
1791 // AA will remain AA
1792 SkRect aaRect = {0.25f, 12.43f, 25.2f, 23.f};
1793 cs.clipRect(SkMatrix::I(), aaRect, GrAA::kYes, SkClipOp::kIntersect);
1794
1795 // Non-AA will become AA
1796 SkPath nonAAPath = make_octagon({2.f, 10.f, 16.f, 20.f});
1797 cs.clipPath(SkMatrix::I(), nonAAPath, GrAA::kNo, SkClipOp::kIntersect);
1798
1799 // Non-AA rects remain non-AA so they can be applied as a scissor
1800 SkRect nonAARect = {4.5f, 5.f, 17.25f, 18.23f};
1801 cs.clipRect(SkMatrix::I(), nonAARect, GrAA::kNo, SkClipOp::kIntersect);
1802
1803 // The stack reports elements newest first, but the non-AA rect op was combined in place with
1804 // the first aa rect, so we should see nonAAPath as AA, and then the intersection of rects.
1805 auto elements = cs.begin();
1806
1807 const ClipStack::Element& nonAARectElement = *elements;
1808 REPORTER_ASSERT(r, nonAARectElement.fShape.isRect(), "Expected rect element");
1809 REPORTER_ASSERT(r, nonAARectElement.fAA == GrAA::kNo,
1810 "Axis-aligned non-AA rect ignores forceAA");
1811 REPORTER_ASSERT(r, nonAARectElement.fShape.rect() == nonAARect,
1812 "Mixed AA rects should not combine");
1813
1814 ++elements;
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");
1818 REPORTER_ASSERT(r, aaPathElement.fAA == GrAA::kYes, "Path element not promoted to AA");
1819
1820 ++elements;
1821 const ClipStack::Element& aaRectElement = *elements;
1822 REPORTER_ASSERT(r, aaRectElement.fShape.isRect(), "Expected rect element");
1823 REPORTER_ASSERT(r, aaRectElement.fShape.rect() == aaRect,
1824 "Mixed AA rects should not combine");
1825 REPORTER_ASSERT(r, aaRectElement.fAA == GrAA::kYes, "Rect element stays AA");
1826
1827 ++elements;
1828 REPORTER_ASSERT(r, !(elements != cs.end()), "Expected only three clip elements");
1829}

◆ DEF_TEST() [11/32]

DEF_TEST ( ClipStack_InitialState  ,
 
)

Definition at line 537 of file GrClipStackTest.cpp.

537 {
538 run_test_case(r, TestCase::Build("initial-state", SkIRect::MakeWH(100, 100)).finishTest());
539}
static constexpr SkIRect MakeWH(int32_t w, int32_t h)
Definition: SkRect.h:56

◆ DEF_TEST() [12/32]

DEF_TEST ( ClipStack_InverseFilledPath  ,
 
)

Definition at line 935 of file GrClipStackTest.cpp.

935 {
936 using ClipState = skgpu::ganesh::ClipStack::ClipState;
937
938 SkRect rect = {0.f, 0.f, 16.f, 17.f};
939 SkPath rectPath;
940 rectPath.addRect(rect);
941
942 SkPath inverseRectPath = rectPath;
943 inverseRectPath.toggleInverseFillType();
944
945 SkPath complexPath = make_octagon(rect);
946 SkPath inverseComplexPath = complexPath;
947 inverseComplexPath.toggleInverseFillType();
948
949 // Inverse filled rect + intersect -> diff rect
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)
954 .finishTest());
955
956 // Inverse filled rect + difference -> int. rect
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()
961 .finishTest());
962
963 // Inverse filled path + intersect -> diff path
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)
968 .finishTest());
969
970 // Inverse filled path + difference -> int. path
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)
975 .finishTest());
976}
void toggleInverseFillType()
Definition: SkPath.h:249
SkPath & addRect(const SkRect &rect, SkPathDirection dir, unsigned start)
Definition: SkPath.cpp:864

◆ DEF_TEST() [13/32]

DEF_TEST ( ClipStack_MixedAAContains  ,
 
)

Definition at line 1484 of file GrClipStackTest.cpp.

1484 {
1485 using ClipState = skgpu::ganesh::ClipStack::ClipState;
1486
1487 SkMatrix lm1 = SkMatrix::RotateDeg(45.f);
1488 SkRect r1 = {-20.f, -20.f, 20.f, 20.f};
1489
1490 SkMatrix lm2 = SkMatrix::RotateDeg(-45.f);
1491 SkRect r2Safe = {-10.f, -10.f, 10.f, 10.f};
1492 SkRect r2Unsafe = {-19.5f, -19.5f, 19.5f, 19.5f};
1493
1494 // Non-AA sufficiently inside AA element can discard the outer AA element
1495 run_test_case(r, TestCase::Build("mixed-outeraa-combine", kDeviceBounds)
1496 .actual().rect(r1, lm1, GrAA::kYes, SkClipOp::kIntersect)
1497 .rect(r2Safe, lm2, GrAA::kNo, SkClipOp::kIntersect)
1498 .finishElements()
1499 .expect().rect(r2Safe, lm2, GrAA::kNo, SkClipOp::kIntersect)
1500 .finishElements()
1501 .state(ClipState::kComplex)
1502 .finishTest());
1503 // Vice versa
1504 run_test_case(r, TestCase::Build("mixed-inneraa-combine", kDeviceBounds)
1505 .actual().rect(r1, lm1, GrAA::kNo, SkClipOp::kIntersect)
1506 .rect(r2Safe, lm2, GrAA::kYes, SkClipOp::kIntersect)
1507 .finishElements()
1508 .expect().rect(r2Safe, lm2, GrAA::kYes, SkClipOp::kIntersect)
1509 .finishElements()
1510 .state(ClipState::kComplex)
1511 .finishTest());
1512
1513 // Non-AA too close to AA edges keeps both
1514 run_test_case(r, TestCase::Build("mixed-outeraa-nocombine", kDeviceBounds)
1515 .actual().rect(r1, lm1, GrAA::kYes, SkClipOp::kIntersect)
1516 .rect(r2Unsafe, lm2, GrAA::kNo, SkClipOp::kIntersect)
1517 .finishElements()
1518 .expectActual()
1519 .state(ClipState::kComplex)
1520 .finishTest());
1521 run_test_case(r, TestCase::Build("mixed-inneraa-nocombine", kDeviceBounds)
1522 .actual().rect(r1, lm1, GrAA::kNo, SkClipOp::kIntersect)
1523 .rect(r2Unsafe, lm2, GrAA::kYes, SkClipOp::kIntersect)
1524 .finishElements()
1525 .expectActual()
1526 .state(ClipState::kComplex)
1527 .finishTest());
1528}

◆ DEF_TEST() [14/32]

DEF_TEST ( ClipStack_MultiplePaths  ,
 
)

Definition at line 1108 of file GrClipStackTest.cpp.

1108 {
1109 using ClipState = skgpu::ganesh::ClipStack::ClipState;
1110
1111 // Chosen to be greater than the number of inline-allocated elements and save records of the
1112 // ClipStack so that we test heap allocation as well.
1113 static constexpr int kNumOps = 16;
1114
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) {
1118 b.actual().path(make_octagon(d), GrAA::kNo, SkClipOp::kDifference);
1119
1120 d.offset(15.f, 0.f);
1121 if (d.fRight > kDeviceBounds.fRight) {
1122 d.fLeft = 0.f;
1123 d.fRight = 12.f;
1124 d.offset(0.f, 15.f);
1125 }
1126 }
1127
1128 run_test_case(r, b.expectActual()
1129 .state(ClipState::kComplex)
1130 .finishTest());
1131
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) {
1135 b.actual().path(make_octagon(d), GrAA::kYes, SkClipOp::kIntersect);
1136 d.offset(0.01f, 0.01f);
1137 }
1138
1139 run_test_case(r, b.expectActual()
1140 .state(ClipState::kComplex)
1141 .finishTest());
1142}
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition: main.cc:19

◆ DEF_TEST() [15/32]

DEF_TEST ( ClipStack_NoDifferenceInterference  ,
 
)

Definition at line 1087 of file GrClipStackTest.cpp.

1087 {
1088 using ClipState = skgpu::ganesh::ClipStack::ClipState;
1089
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};
1094
1095 run_test_case(r, TestCase::Build("cross-diff-combine", kDeviceBounds)
1096 .actual().rect(intR1, GrAA::kYes, SkClipOp::kIntersect)
1099 .finishElements()
1100 .expect().rect(intCombo, GrAA::kYes, SkClipOp::kIntersect)
1102 .finishElements()
1103 .state(ClipState::kComplex)
1104 .finishTest());
1105}

◆ DEF_TEST() [16/32]

DEF_TEST ( ClipStack_NonAxisAlignedContains  ,
 
)

Definition at line 1346 of file GrClipStackTest.cpp.

1346 {
1347 using ClipState = skgpu::ganesh::ClipStack::ClipState;
1348
1349 SkMatrix lm1 = SkMatrix::RotateDeg(45.f);
1350 SkRect bigR = {-20.f, -20.f, 20.f, 20.f};
1351 SkRRect bigRR = SkRRect::MakeRectXY(bigR, 1.f, 1.f);
1352
1353 SkMatrix lm2 = SkMatrix::RotateDeg(-45.f);
1354 SkRect smR = {-10.f, -10.f, 10.f, 10.f};
1355 SkRRect smRR = SkRRect::MakeRectXY(smR, 1.f, 1.f);
1356
1357 // I+I should select the smaller 2nd shape (r2 or rr2)
1358 run_test_case(r, TestCase::Build("rect-rect-ii", kDeviceBounds)
1359 .actual().rect(bigR, lm1, GrAA::kYes, SkClipOp::kIntersect)
1361 .finishElements()
1362 .expect().rect(smR, lm2, GrAA::kYes, SkClipOp::kIntersect)
1363 .finishElements()
1364 .state(ClipState::kComplex)
1365 .finishTest());
1366 run_test_case(r, TestCase::Build("rrect-rrect-ii", kDeviceBounds)
1367 .actual().rrect(bigRR, lm1, GrAA::kYes, SkClipOp::kIntersect)
1369 .finishElements()
1370 .expect().rrect(smRR, lm2, GrAA::kYes, SkClipOp::kIntersect)
1371 .finishElements()
1372 .state(ClipState::kComplex)
1373 .finishTest());
1374 run_test_case(r, TestCase::Build("rect-rrect-ii", kDeviceBounds)
1375 .actual().rect(bigR, lm1, GrAA::kYes, SkClipOp::kIntersect)
1377 .finishElements()
1378 .expect().rrect(smRR, lm2, GrAA::kYes, SkClipOp::kIntersect)
1379 .finishElements()
1380 .state(ClipState::kComplex)
1381 .finishTest());
1382 run_test_case(r, TestCase::Build("rrect-rect-ii", kDeviceBounds)
1383 .actual().rrect(bigRR, lm1, GrAA::kYes, SkClipOp::kIntersect)
1385 .finishElements()
1386 .expect().rect(smR, lm2, GrAA::kYes, SkClipOp::kIntersect)
1387 .finishElements()
1388 .state(ClipState::kComplex)
1389 .finishTest());
1390
1391 // D+D should select the larger shape (r1 or rr1)
1392 run_test_case(r, TestCase::Build("rect-rect-dd", kDeviceBounds)
1393 .actual().rect(bigR, lm1, GrAA::kYes, SkClipOp::kDifference)
1395 .finishElements()
1396 .expect().rect(bigR, lm1, GrAA::kYes, SkClipOp::kDifference)
1397 .finishElements()
1398 .state(ClipState::kComplex)
1399 .finishTest());
1400 run_test_case(r, TestCase::Build("rrect-rrect-dd", kDeviceBounds)
1401 .actual().rrect(bigRR, lm1, GrAA::kYes, SkClipOp::kDifference)
1403 .finishElements()
1404 .expect().rrect(bigRR, lm1, GrAA::kYes, SkClipOp::kDifference)
1405 .finishElements()
1406 .state(ClipState::kComplex)
1407 .finishTest());
1408 run_test_case(r, TestCase::Build("rect-rrect-dd", kDeviceBounds)
1409 .actual().rect(bigR, lm1, GrAA::kYes, SkClipOp::kDifference)
1411 .finishElements()
1412 .expect().rect(bigR, lm1, GrAA::kYes, SkClipOp::kDifference)
1413 .finishElements()
1414 .state(ClipState::kComplex)
1415 .finishTest());
1416 run_test_case(r, TestCase::Build("rrect-rect-dd", kDeviceBounds)
1417 .actual().rrect(bigRR, lm1, GrAA::kYes, SkClipOp::kDifference)
1419 .finishElements()
1420 .expect().rrect(bigRR, lm1, GrAA::kYes, SkClipOp::kDifference)
1421 .finishElements()
1422 .state(ClipState::kComplex)
1423 .finishTest());
1424
1425 // D(1)+I(2) should result in empty
1426 run_test_case(r, TestCase::Build("rectD-rectI", kDeviceBounds)
1427 .actual().rect(bigR, lm1, GrAA::kYes, SkClipOp::kDifference)
1429 .finishElements()
1431 .finishTest());
1432 run_test_case(r, TestCase::Build("rrectD-rrectI", kDeviceBounds)
1433 .actual().rrect(bigRR, lm1, GrAA::kYes, SkClipOp::kDifference)
1435 .finishElements()
1437 .finishTest());
1438 run_test_case(r, TestCase::Build("rectD-rrectI", kDeviceBounds)
1439 .actual().rect(bigR, lm1, GrAA::kYes, SkClipOp::kDifference)
1441 .finishElements()
1443 .finishTest());
1444 run_test_case(r, TestCase::Build("rrectD-rectI", kDeviceBounds)
1445 .actual().rrect(bigRR, lm1, GrAA::kYes, SkClipOp::kDifference)
1447 .finishElements()
1449 .finishTest());
1450
1451 // I(1)+D(2) should result in both shapes
1452 run_test_case(r, TestCase::Build("rectI+rectD", kDeviceBounds)
1453 .actual().rect(bigR, lm1, GrAA::kYes, SkClipOp::kIntersect)
1455 .finishElements()
1456 .expectActual()
1457 .state(ClipState::kComplex)
1458 .finishTest());
1459 run_test_case(r, TestCase::Build("rrectI+rrectD", kDeviceBounds)
1460 .actual().rrect(bigRR, lm1, GrAA::kYes, SkClipOp::kIntersect)
1462 .finishElements()
1463 .expectActual()
1464 .state(ClipState::kComplex)
1465 .finishTest());
1466 run_test_case(r, TestCase::Build("rrectI+rectD", kDeviceBounds)
1467 .actual().rrect(bigRR, lm1, GrAA::kYes, SkClipOp::kIntersect)
1469 .finishElements()
1470 .expectActual()
1471 .state(ClipState::kComplex)
1472 .finishTest());
1473 run_test_case(r, TestCase::Build("rectI+rrectD", kDeviceBounds)
1474 .actual().rect(bigR, lm1, GrAA::kYes, SkClipOp::kIntersect)
1476 .finishElements()
1477 .expectActual()
1478 .state(ClipState::kComplex)
1479 .finishTest());
1480}

◆ DEF_TEST() [17/32]

DEF_TEST ( ClipStack_Offscreen  ,
 
)

Definition at line 979 of file GrClipStackTest.cpp.

979 {
980 using ClipState = skgpu::ganesh::ClipStack::ClipState;
981
982 SkRect offscreenRect = {kDeviceBounds.fRight + 10.f, kDeviceBounds.fTop + 20.f,
983 kDeviceBounds.fRight + 40.f, kDeviceBounds.fTop + 60.f};
984 SkASSERT(!offscreenRect.intersects(SkRect::Make(kDeviceBounds)));
985
986 SkRRect offscreenRRect = SkRRect::MakeRectXY(offscreenRect, 5.f, 5.f);
987 SkPath offscreenPath = make_octagon(offscreenRect);
988
989 // Intersect -> empty
990 run_test_case(r, TestCase::Build("intersect-combo", kDeviceBounds)
991 .actual().aa().intersect()
992 .rect(offscreenRect)
993 .rrect(offscreenRRect)
994 .path(offscreenPath)
995 .finishElements()
997 .finishTest());
998 run_test_case(r, TestCase::Build("intersect-rect", kDeviceBounds)
999 .actual().aa().intersect()
1000 .rect(offscreenRect)
1001 .finishElements()
1003 .finishTest());
1004 run_test_case(r, TestCase::Build("intersect-rrect", kDeviceBounds)
1005 .actual().aa().intersect()
1006 .rrect(offscreenRRect)
1007 .finishElements()
1009 .finishTest());
1010 run_test_case(r, TestCase::Build("intersect-path", kDeviceBounds)
1011 .actual().aa().intersect()
1012 .path(offscreenPath)
1013 .finishElements()
1015 .finishTest());
1016
1017 // Difference -> wide open
1018 run_test_case(r, TestCase::Build("difference-combo", kDeviceBounds)
1019 .actual().aa().difference()
1020 .rect(offscreenRect)
1021 .rrect(offscreenRRect)
1022 .path(offscreenPath)
1023 .finishElements()
1024 .state(ClipState::kWideOpen)
1025 .finishTest());
1026 run_test_case(r, TestCase::Build("difference-rect", kDeviceBounds)
1027 .actual().aa().difference()
1028 .rect(offscreenRect)
1029 .finishElements()
1030 .state(ClipState::kWideOpen)
1031 .finishTest());
1032 run_test_case(r, TestCase::Build("difference-rrect", kDeviceBounds)
1033 .actual().aa().difference()
1034 .rrect(offscreenRRect)
1035 .finishElements()
1036 .state(ClipState::kWideOpen)
1037 .finishTest());
1038 run_test_case(r, TestCase::Build("difference-path", kDeviceBounds)
1039 .actual().aa().difference()
1040 .path(offscreenPath)
1041 .finishElements()
1042 .state(ClipState::kWideOpen)
1043 .finishTest());
1044}

◆ DEF_TEST() [18/32]

DEF_TEST ( ClipStack_PathSimplify  ,
 
)

Definition at line 808 of file GrClipStackTest.cpp.

808 {
809 using ClipState = skgpu::ganesh::ClipStack::ClipState;
810
811 // Empty, point, and line paths -> empty
813 run_test_case(r, TestCase::Build("empty", kDeviceBounds)
814 .actual().path(empty).finishElements()
816 .finishTest());
817 SkPath point;
818 point.moveTo({0.f, 0.f});
819 run_test_case(r, TestCase::Build("point", kDeviceBounds)
820 .actual().path(point).finishElements()
822 .finishTest());
823
824 SkPath line;
825 line.moveTo({0.f, 0.f});
826 line.lineTo({10.f, 5.f});
827 run_test_case(r, TestCase::Build("line", kDeviceBounds)
828 .actual().path(line).finishElements()
830 .finishTest());
831
832 // Rect path -> rect element
833 SkRect rect = {0.f, 2.f, 10.f, 15.4f};
834 SkPath rectPath;
835 rectPath.addRect(rect);
836 run_test_case(r, TestCase::Build("rect", kDeviceBounds)
837 .actual().path(rectPath).finishElements()
838 .expect().rect(rect).finishElements()
840 .finishTest());
841
842 // Oval path -> rrect element
843 SkPath ovalPath;
844 ovalPath.addOval(rect);
845 run_test_case(r, TestCase::Build("oval", kDeviceBounds)
846 .actual().path(ovalPath).finishElements()
847 .expect().rrect(SkRRect::MakeOval(rect)).finishElements()
848 .state(ClipState::kDeviceRRect)
849 .finishTest());
850
851 // RRect path -> rrect element
853 SkPath rrectPath;
854 rrectPath.addRRect(rrect);
855 run_test_case(r, TestCase::Build("rrect", kDeviceBounds)
856 .actual().path(rrectPath).finishElements()
857 .expect().rrect(rrect).finishElements()
858 .state(ClipState::kDeviceRRect)
859 .finishTest());
860}
SkPath & moveTo(SkScalar x, SkScalar y)
Definition: SkPath.cpp:688
SkPath & addRRect(const SkRRect &rrect, SkPathDirection dir=SkPathDirection::kCW)
Definition: SkPath.cpp:1000
SkPath & addOval(const SkRect &oval, SkPathDirection dir=SkPathDirection::kCW)
Definition: SkPath.cpp:1106
EMSCRIPTEN_KEEPALIVE void empty()

◆ DEF_TEST() [19/32]

DEF_TEST ( ClipStack_PreApply  ,
 
)

Definition at line 1833 of file GrClipStackTest.cpp.

1833 {
1834 using ClipStack = skgpu::ganesh::ClipStack;
1835
1836 ClipStack cs(kDeviceBounds, nullptr, false);
1837
1838 // Offscreen is kClippedOut
1839 GrClip::PreClipResult result = cs.preApply({-10.f, -10.f, -1.f, -1.f}, GrAA::kYes);
1841 "Offscreen draw is kClippedOut");
1842
1843 // Intersecting screen with wide-open clip is kUnclipped
1844 result = cs.preApply({-10.f, -10.f, 10.f, 10.f}, GrAA::kYes);
1846 "Wide open screen intersection is still kUnclipped");
1847
1848 // Empty clip is clipped out
1849 cs.save();
1851 result = cs.preApply({0.f, 0.f, 20.f, 20.f}, GrAA::kYes);
1853 "Empty clip stack preApplies as kClippedOut");
1854 cs.restore();
1855
1856 // Contained inside clip is kUnclipped (using rrect for the outer clip element since paths
1857 // don't support an inner bounds and anything complex is otherwise skipped in preApply).
1858 SkRect rect = {10.f, 10.f, 40.f, 40.f};
1859 SkRRect bigRRect = SkRRect::MakeRectXY(rect.makeOutset(5.f, 5.f), 5.f, 5.f);
1860 cs.save();
1861 cs.clipRRect(SkMatrix::I(), bigRRect, GrAA::kYes, SkClipOp::kIntersect);
1862 result = cs.preApply(rect, GrAA::kYes);
1864 "Draw contained within clip is kUnclipped");
1865
1866 // Disjoint from clip (but still on screen) is kClippedOut
1867 result = cs.preApply({50.f, 50.f, 60.f, 60.f}, GrAA::kYes);
1869 "Draw not intersecting clip is kClippedOut");
1870 cs.restore();
1871
1872 // Intersecting clip is kClipped for complex shape
1873 cs.save();
1874 SkPath path = make_octagon(rect.makeOutset(5.f, 5.f), 5.f, 5.f);
1876 result = cs.preApply(path.getBounds(), GrAA::kNo);
1877 REPORTER_ASSERT(r, result.fEffect == GrClip::Effect::kClipped && !result.fIsRRect,
1878 "Draw with complex clip is kClipped, but is not an rrect");
1879 cs.restore();
1880
1881 // Intersecting clip is kDeviceRect for axis-aligned rect clip
1882 cs.save();
1884 result = cs.preApply(rect.makeOffset(2.f, 2.f), GrAA::kNo);
1886 result.fAA == GrAA::kYes &&
1887 result.fIsRRect &&
1888 result.fRRect == SkRRect::MakeRect(rect),
1889 "kDeviceRect clip stack should be reported by preApply");
1890 cs.restore();
1891
1892 // Intersecting clip is kDeviceRRect for axis-aligned rrect clip
1893 cs.save();
1896 result = cs.preApply(rect.makeOffset(2.f, 2.f), GrAA::kNo);
1898 result.fAA == GrAA::kYes &&
1899 result.fIsRRect &&
1900 result.fRRect == clipRRect,
1901 "kDeviceRRect clip stack should be reported by preApply");
1902 cs.restore();
1903}
GAsyncResult * result
clipRRect(r.rrect, r.opAA.op(), r.opAA.aa())) DRAW(ClipRect

◆ DEF_TEST() [20/32]

DEF_TEST ( ClipStack_PreserveAxisAlignment  ,
 
)

Definition at line 1245 of file GrClipStackTest.cpp.

1245 {
1246 using ClipState = skgpu::ganesh::ClipStack::ClipState;
1247
1248 SkMatrix lm = SkMatrix::RotateDeg(90.f);
1249 lm.postTranslate(15.5f, 14.3f);
1251
1252 // Rect -> matrix is applied up front
1253 SkRect rect = {0.f, 0.f, 10.f, 10.f};
1254 run_test_case(r, TestCase::Build("r90+rect", kDeviceBounds)
1255 .actual().rect(rect, lm, GrAA::kYes, SkClipOp::kIntersect)
1256 .finishElements()
1258 .finishElements()
1260 .finishTest());
1261
1262 // RRect -> matrix is applied up front
1263 SkRRect localRRect = SkRRect::MakeRectXY(rect, 2.f, 2.f);
1264 SkRRect deviceRRect;
1265 SkAssertResult(localRRect.transform(lm, &deviceRRect));
1266 run_test_case(r, TestCase::Build("r90+rrect", kDeviceBounds)
1267 .actual().rrect(localRRect, lm, GrAA::kYes, SkClipOp::kIntersect)
1268 .finishElements()
1269 .expect().rrect(deviceRRect, GrAA::kYes, SkClipOp::kIntersect)
1270 .finishElements()
1271 .state(ClipState::kDeviceRRect)
1272 .finishTest());
1273
1274 // Path -> matrix is NOT applied
1275 run_test_case(r, TestCase::Build("r90+path", kDeviceBounds)
1276 .actual().intersect().localToDevice(lm).path(make_octagon(rect))
1277 .finishElements()
1278 .expectActual()
1279 .state(ClipState::kComplex)
1280 .finishTest());
1281}
SkMatrix & postTranslate(SkScalar dx, SkScalar dy)
Definition: SkMatrix.cpp:281
bool isScaleTranslate() const
Definition: SkMatrix.h:236
bool preservesAxisAlignment() const
Definition: SkMatrix.h:299
bool mapRect(SkRect *dst, const SkRect &src, SkApplyPerspectiveClip pc=SkApplyPerspectiveClip::kYes) const
Definition: SkMatrix.cpp:1141
bool transform(const SkMatrix &matrix, SkRRect *dst) const
Definition: SkRRect.cpp:436

◆ DEF_TEST() [21/32]

DEF_TEST ( ClipStack_RectDeviceClip  ,
 
)

Definition at line 761 of file GrClipStackTest.cpp.

761 {
762 using ClipState = skgpu::ganesh::ClipStack::ClipState;
763
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};
767
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()
772 .finishTest());
773
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()
778 .finishTest());
779}
SkScalar fTop
smaller y-axis bounds
Definition: extension.cpp:15

◆ DEF_TEST() [22/32]

DEF_TEST ( ClipStack_RectRectAACombine  ,
 
)

Definition at line 543 of file GrClipStackTest.cpp.

543 {
544 using ClipState = skgpu::ganesh::ClipStack::ClipState;
545
546 SkRect pixelAligned = {0, 0, 10, 10};
547 SkRect fracRect1 = pixelAligned.makeOffset(5.3f, 3.7f);
548 SkRect fracRect2 = {fracRect1.fLeft + 0.75f * fracRect1.width(),
549 fracRect1.fTop + 0.75f * fracRect1.height(),
550 fracRect1.fRight, fracRect1.fBottom};
551
552 SkRect fracIntersect;
553 SkAssertResult(fracIntersect.intersect(fracRect1, fracRect2));
554 SkRect alignedIntersect;
555 SkAssertResult(alignedIntersect.intersect(pixelAligned, fracRect1));
556
557 // Both AA combine to one element
558 run_test_case(r, TestCase::Build("aa", kDeviceBounds)
559 .actual().aa().intersect()
560 .rect(fracRect1).rect(fracRect2)
561 .finishElements()
562 .expect().aa().intersect().rect(fracIntersect).finishElements()
564 .finishTest());
565
566 // Both non-AA combine to one element
567 run_test_case(r, TestCase::Build("nonaa", kDeviceBounds)
568 .actual().nonAA().intersect()
569 .rect(fracRect1).rect(fracRect2)
570 .finishElements()
571 .expect().nonAA().intersect().rect(fracIntersect).finishElements()
573 .finishTest());
574
575 // Pixel-aligned AA and non-AA combine
576 run_test_case(r, TestCase::Build("aligned-aa+nonaa", kDeviceBounds)
577 .actual().intersect()
578 .aa().rect(pixelAligned).nonAA().rect(fracRect1)
579 .finishElements()
580 .expect().nonAA().intersect().rect(alignedIntersect).finishElements()
582 .finishTest());
583
584 // AA and pixel-aligned non-AA combine
585 run_test_case(r, TestCase::Build("aa+aligned-nonaa", kDeviceBounds)
586 .actual().intersect()
587 .aa().rect(fracRect1).nonAA().rect(pixelAligned)
588 .finishElements()
589 .expect().aa().intersect().rect(alignedIntersect).finishElements()
591 .finishTest());
592
593 // Other mixed AA modes do not combine
594 run_test_case(r, TestCase::Build("aa+nonaa", kDeviceBounds)
595 .actual().intersect()
596 .aa().rect(fracRect1).nonAA().rect(fracRect2)
597 .finishElements()
598 .expectActual()
599 .state(ClipState::kComplex)
600 .finishTest());
601}
SkScalar fBottom
larger y-axis bounds
Definition: extension.cpp:17
SkScalar fLeft
smaller x-axis bounds
Definition: extension.cpp:14
constexpr float height() const
Definition: SkRect.h:769
constexpr float width() const
Definition: SkRect.h:762

◆ DEF_TEST() [23/32]

DEF_TEST ( ClipStack_RectRectNonAxisAligned  ,
 
)

Definition at line 623 of file GrClipStackTest.cpp.

623 {
624 using ClipState = skgpu::ganesh::ClipStack::ClipState;
625
626 SkRect pixelAligned = {0, 0, 10, 10};
627 SkRect fracRect1 = pixelAligned.makeOffset(5.3f, 3.7f);
628 SkRect fracRect2 = {fracRect1.fLeft + 0.75f * fracRect1.width(),
629 fracRect1.fTop + 0.75f * fracRect1.height(),
630 fracRect1.fRight, fracRect1.fBottom};
631
632 SkRect fracIntersect;
633 SkAssertResult(fracIntersect.intersect(fracRect1, fracRect2));
634
635 SkMatrix lm = SkMatrix::RotateDeg(45.f);
636
637 // Both AA combine
638 run_test_case(r, TestCase::Build("aa", kDeviceBounds)
639 .actual().aa().intersect().localToDevice(lm)
640 .rect(fracRect1).rect(fracRect2)
641 .finishElements()
642 .expect().aa().intersect().localToDevice(lm)
643 .rect(fracIntersect).finishElements()
644 .state(ClipState::kComplex)
645 .finishTest());
646
647 // Both non-AA combine
648 run_test_case(r, TestCase::Build("nonaa", kDeviceBounds)
649 .actual().nonAA().intersect().localToDevice(lm)
650 .rect(fracRect1).rect(fracRect2)
651 .finishElements()
652 .expect().nonAA().intersect().localToDevice(lm)
653 .rect(fracIntersect).finishElements()
654 .state(ClipState::kComplex)
655 .finishTest());
656
657 // Integer-aligned coordinates under a local matrix with mixed AA don't combine, though
658 run_test_case(r, TestCase::Build("local-aa", kDeviceBounds)
659 .actual().intersect().localToDevice(lm)
660 .aa().rect(pixelAligned).nonAA().rect(fracRect1)
661 .finishElements()
662 .expectActual()
663 .state(ClipState::kComplex)
664 .finishTest());
665}

◆ DEF_TEST() [24/32]

DEF_TEST ( ClipStack_RectRRectCombine  ,
 
)

Definition at line 726 of file GrClipStackTest.cpp.

726 {
727 using ClipState = skgpu::ganesh::ClipStack::ClipState;
728
729 SkRRect rrect = SkRRect::MakeRectXY({0, 0, 10, 10}, 2.f, 2.f);
730 SkRect cutTop = {-10, -10, 10, 4};
731 SkRect cutMid = {-10, 3, 10, 7};
732
733 // Rect + RRect becomes a round rect with some square corners
734 SkVector cutCorners[4] = {{2.f, 2.f}, {2.f, 2.f}, {0, 0}, {0, 0}};
735 SkRRect cutRRect;
736 cutRRect.setRectRadii({0, 0, 10, 4}, cutCorners);
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)
741 .finishTest());
742
743 // Rect + RRect becomes a rect
744 SkRect cutRect = {0, 3, 10, 7};
745 run_test_case(r, TestCase::Build("to-rect", kDeviceBounds)
746 .actual().intersect().aa().rrect(rrect).rect(cutMid).finishElements()
747 .expect().intersect().aa().rect(cutRect).finishElements()
749 .finishTest());
750
751 // But they can only combine when the intersecting shape is representable as a [r]rect.
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()
755 .expectActual()
756 .state(ClipState::kComplex)
757 .finishTest());
758}
void setRectRadii(const SkRect &rect, const SkVector radii[4])
Definition: SkRRect.cpp:189

◆ DEF_TEST() [25/32]

DEF_TEST ( ClipStack_RepeatElement  ,
 
)

Definition at line 863 of file GrClipStackTest.cpp.

863 {
864 using ClipState = skgpu::ganesh::ClipStack::ClipState;
865
866 // Same rect
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()
872 .finishTest());
873 SkMatrix lm;
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)
877 .finishElements()
878 .expect().localToDevice(lm).rect(rect).finishElements()
879 .state(ClipState::kComplex)
880 .finishTest());
881
882 // Same rrect
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)
888 .finishTest());
889 run_test_case(r, TestCase::Build("same-local-rrects", kDeviceBounds)
890 .actual().localToDevice(lm).rrect(rrect).rrect(rrect).rrect(rrect)
891 .finishElements()
892 .expect().localToDevice(lm).rrect(rrect).finishElements()
893 .state(ClipState::kComplex)
894 .finishTest());
895
896 // Same convex path, by ==
897 run_test_case(r, TestCase::Build("same-convex", kDeviceBounds)
898 .actual().path(make_octagon(rect)).path(make_octagon(rect))
899 .finishElements()
900 .expect().path(make_octagon(rect)).finishElements()
901 .state(ClipState::kComplex)
902 .finishTest());
903 run_test_case(r, TestCase::Build("same-local-convex", kDeviceBounds)
904 .actual().localToDevice(lm)
905 .path(make_octagon(rect)).path(make_octagon(rect))
906 .finishElements()
907 .expect().localToDevice(lm).path(make_octagon(rect))
908 .finishElements()
909 .state(ClipState::kComplex)
910 .finishTest());
911
912 // Same complicated path by gen-id but not ==
913 SkPath path; // an hour glass
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});
918 path.close();
919
920 run_test_case(r, TestCase::Build("same-path", kDeviceBounds)
921 .actual().path(path).path(path).path(path).finishElements()
922 .expect().path(path).finishElements()
923 .state(ClipState::kComplex)
924 .finishTest());
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)
929 .finishElements()
930 .state(ClipState::kComplex)
931 .finishTest());
932}
SkMatrix & setRotate(SkScalar degrees, SkScalar px, SkScalar py)
Definition: SkMatrix.cpp:452

◆ DEF_TEST() [26/32]

DEF_TEST ( ClipStack_ReplaceClip  ,
 
)

Definition at line 1716 of file GrClipStackTest.cpp.

1716 {
1717 using ClipStack = skgpu::ganesh::ClipStack;
1718
1719 ClipStack cs(kDeviceBounds, nullptr, false);
1720
1721 SkRRect rrect = SkRRect::MakeRectXY({15.f, 12.25f, 40.3f, 23.5f}, 4.f, 6.f);
1723
1724 SkIRect replace = {50, 25, 75, 40}; // Is disjoint from the rrect element
1725 cs.save();
1726 cs.replaceClip(replace);
1727
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();
1732 REPORTER_ASSERT(r, replaceElement.fShape.rect() == SkRect::Make(replace) &&
1733 replaceElement.fAA == GrAA::kNo &&
1734 replaceElement.fOp == SkClipOp::kIntersect &&
1735 replaceElement.fLocalToDevice == SkMatrix::I(),
1736 "Unexpected replace element state");
1737
1738 // Restore should undo the replaced clip and bring back the rrect
1739 cs.restore();
1740 REPORTER_ASSERT(r, cs.clipState() == ClipStack::ClipState::kDeviceRRect,
1741 "Unexpected state after restore, not kDeviceRRect");
1742 const ClipStack::Element& rrectElem = *cs.begin();
1743 REPORTER_ASSERT(r, rrectElem.fShape.rrect() == rrect &&
1744 rrectElem.fAA == GrAA::kYes &&
1745 rrectElem.fOp == SkClipOp::kIntersect &&
1746 rrectElem.fLocalToDevice == SkMatrix::I(),
1747 "RRect element state not restored properly after replace clip undone");
1748}
Definition: SkRect.h:32

◆ DEF_TEST() [27/32]

DEF_TEST ( ClipStack_RRectRRectAACombine  ,
 
)

Definition at line 669 of file GrClipStackTest.cpp.

669 {
670 using ClipState = skgpu::ganesh::ClipStack::ClipState;
671
672 SkRRect r1 = SkRRect::MakeRectXY(SkRect::MakeWH(12, 12), 2.f, 2.f);
673 SkRRect r2 = r1.makeOffset(6.f, 6.f);
674
676 SkASSERT(!intersect.isEmpty());
677
678 // Both AA combine
679 run_test_case(r, TestCase::Build("aa", kDeviceBounds)
680 .actual().aa().intersect()
681 .rrect(r1).rrect(r2)
682 .finishElements()
683 .expect().aa().intersect().rrect(intersect).finishElements()
684 .state(ClipState::kDeviceRRect)
685 .finishTest());
686
687 // Both non-AA combine
688 run_test_case(r, TestCase::Build("nonaa", kDeviceBounds)
689 .actual().nonAA().intersect()
690 .rrect(r1).rrect(r2)
691 .finishElements()
692 .expect().nonAA().intersect().rrect(intersect).finishElements()
693 .state(ClipState::kDeviceRRect)
694 .finishTest());
695
696 // Mixed do not combine
697 run_test_case(r, TestCase::Build("aa+nonaa", kDeviceBounds)
698 .actual().intersect()
699 .aa().rrect(r1).nonAA().rrect(r2)
700 .finishElements()
701 .expectActual()
702 .state(ClipState::kComplex)
703 .finishTest());
704
705 // Same AA state can combine in the same local coordinate space
706 SkMatrix lm = SkMatrix::RotateDeg(45.f);
707 run_test_case(r, TestCase::Build("local-aa", kDeviceBounds)
708 .actual().aa().intersect().localToDevice(lm)
709 .rrect(r1).rrect(r2)
710 .finishElements()
711 .expect().aa().intersect().localToDevice(lm)
712 .rrect(intersect).finishElements()
713 .state(ClipState::kComplex)
714 .finishTest());
715 run_test_case(r, TestCase::Build("local-nonaa", kDeviceBounds)
716 .actual().nonAA().intersect().localToDevice(lm)
717 .rrect(r1).rrect(r2)
718 .finishElements()
719 .expect().nonAA().intersect().localToDevice(lm)
720 .rrect(intersect).finishElements()
721 .state(ClipState::kComplex)
722 .finishTest());
723}
SkRRect makeOffset(SkScalar dx, SkScalar dy) const
Definition: SkRRect.h:397
static constexpr SkRect MakeWH(float w, float h)
Definition: SkRect.h:609

◆ DEF_TEST() [28/32]

DEF_TEST ( ClipStack_ScaleTranslate  ,
 
)

Definition at line 1206 of file GrClipStackTest.cpp.

1206 {
1207 using ClipState = skgpu::ganesh::ClipStack::ClipState;
1208
1209 SkMatrix lm = SkMatrix::Scale(2.f, 4.f);
1210 lm.postTranslate(15.5f, 14.3f);
1212
1213 // Rect -> matrix is applied up front
1214 SkRect rect = {0.f, 0.f, 10.f, 10.f};
1215 run_test_case(r, TestCase::Build("st+rect", kDeviceBounds)
1216 .actual().rect(rect, lm, GrAA::kYes, SkClipOp::kIntersect)
1217 .finishElements()
1219 .finishElements()
1221 .finishTest());
1222
1223 // RRect -> matrix is applied up front
1224 SkRRect localRRect = SkRRect::MakeRectXY(rect, 2.f, 2.f);
1225 SkRRect deviceRRect;
1226 SkAssertResult(localRRect.transform(lm, &deviceRRect));
1227 run_test_case(r, TestCase::Build("st+rrect", kDeviceBounds)
1228 .actual().rrect(localRRect, lm, GrAA::kYes, SkClipOp::kIntersect)
1229 .finishElements()
1230 .expect().rrect(deviceRRect, GrAA::kYes, SkClipOp::kIntersect)
1231 .finishElements()
1232 .state(ClipState::kDeviceRRect)
1233 .finishTest());
1234
1235 // Path -> matrix is NOT applied
1236 run_test_case(r, TestCase::Build("st+path", kDeviceBounds)
1237 .actual().intersect().localToDevice(lm).path(make_octagon(rect))
1238 .finishElements()
1239 .expectActual()
1240 .state(ClipState::kComplex)
1241 .finishTest());
1242}
static SkMatrix Scale(SkScalar sx, SkScalar sy)
Definition: SkMatrix.h:75

◆ DEF_TEST() [29/32]

DEF_TEST ( ClipStack_Shader  ,
 
)

Definition at line 1906 of file GrClipStackTest.cpp.

1906 {
1907 using ClipStack = skgpu::ganesh::ClipStack;
1909
1910 sk_sp<SkShader> shader = SkShaders::Color({0.f, 0.f, 0.f, 0.5f}, nullptr);
1911
1913 std::unique_ptr<SurfaceDrawContext> sdc = SurfaceDrawContext::Make(
1915 SkBackingFit::kExact, kDeviceBounds.size(), SkSurfaceProps(),
1916 /*label=*/{});
1917
1918 ClipStack cs(kDeviceBounds, &SkMatrix::I(), false);
1919 cs.save();
1920 cs.clipShader(shader);
1921
1922 REPORTER_ASSERT(r, cs.clipState() == ClipStack::ClipState::kComplex,
1923 "A clip shader should be reported as a complex clip");
1924
1925 GrAppliedClip out(kDeviceBounds.size());
1926 SkRect drawBounds = {10.f, 11.f, 16.f, 32.f};
1927 GrClip::Effect effect = cs.apply(context.get(), sdc.get(), NoOp::Get(), GrAAType::kCoverage,
1928 &out, &drawBounds);
1929
1931 "apply() should return kClipped for a clip shader");
1932 REPORTER_ASSERT(r, out.hasCoverageFragmentProcessor(),
1933 "apply() should have converted clip shader to a coverage FP");
1934
1935 GrAppliedClip out2(kDeviceBounds.size());
1936 drawBounds = {-15.f, -10.f, -1.f, 10.f}; // offscreen
1937 effect = cs.apply(context.get(), sdc.get(), NoOp::Get(), GrAAType::kCoverage, &out2,
1938 &drawBounds);
1940 "apply() should still discard offscreen draws with a clip shader");
1941
1942 cs.restore();
1943 REPORTER_ASSERT(r, cs.clipState() == ClipStack::ClipState::kWideOpen,
1944 "restore() should get rid of the clip shader");
1945
1946
1947 // Adding a clip shader on top of a device rect clip should prevent preApply from reporting
1948 // it as a device rect
1949 cs.clipRect(SkMatrix::I(), {10, 15, 30, 30}, GrAA::kNo, SkClipOp::kIntersect);
1950 SkASSERT(cs.clipState() == ClipStack::ClipState::kDeviceRect); // test precondition
1951 cs.clipShader(shader);
1952 GrClip::PreClipResult result = cs.preApply(SkRect::Make(kDeviceBounds), GrAA::kYes);
1953 REPORTER_ASSERT(r, result.fEffect == GrClip::Effect::kClipped && !result.fIsRRect,
1954 "A clip shader should not produce a device rect from preApply");
1955}
SK_API sk_sp< SkShader > Color(SkColor)

◆ DEF_TEST() [30/32]

DEF_TEST ( ClipStack_ShapeContainsDevice  ,
 
)

Definition at line 1531 of file GrClipStackTest.cpp.

1531 {
1532 using ClipState = skgpu::ganesh::ClipStack::ClipState;
1533
1534 SkRect rect = SkRect::Make(kDeviceBounds).makeOutset(10.f, 10.f);
1535 SkRRect rrect = SkRRect::MakeRectXY(rect, 10.f, 10.f);
1536 SkPath convex = make_octagon(rect, 10.f, 10.f);
1537
1538 // Intersect -> no-op
1539 run_test_case(r, TestCase::Build("rect-intersect", kDeviceBounds)
1540 .actual().intersect().rect(rect).finishElements()
1541 .state(ClipState::kWideOpen)
1542 .finishTest());
1543 run_test_case(r, TestCase::Build("rrect-intersect", kDeviceBounds)
1544 .actual().intersect().rrect(rrect).finishElements()
1545 .state(ClipState::kWideOpen)
1546 .finishTest());
1547 run_test_case(r, TestCase::Build("convex-intersect", kDeviceBounds)
1548 .actual().intersect().path(convex).finishElements()
1549 .state(ClipState::kWideOpen)
1550 .finishTest());
1551
1552 // Difference -> empty
1553 run_test_case(r, TestCase::Build("rect-difference", kDeviceBounds)
1554 .actual().difference().rect(rect).finishElements()
1556 .finishTest());
1557 run_test_case(r, TestCase::Build("rrect-difference", kDeviceBounds)
1558 .actual().difference().rrect(rrect).finishElements()
1560 .finishTest());
1561 run_test_case(r, TestCase::Build("convex-difference", kDeviceBounds)
1562 .actual().difference().path(convex).finishElements()
1564 .finishTest());
1565}
SkRect makeOutset(float dx, float dy) const
Definition: SkRect.h:1002

◆ DEF_TEST() [31/32]

DEF_TEST ( ClipStack_ShapeDeviceBoundsClip  ,
 
)

Definition at line 782 of file GrClipStackTest.cpp.

782 {
783 using ClipState = skgpu::ganesh::ClipStack::ClipState;
784
785 SkRect crossesDeviceEdge = {20.f, kDeviceBounds.fTop - 13.2f,
786 kDeviceBounds.fRight + 15.5f, 30.f};
787
788 // RRect
789 run_test_case(r, TestCase::Build("device-rrect", kDeviceBounds)
790 .actual().intersect().aa()
791 .rrect(SkRRect::MakeRectXY(crossesDeviceEdge, 4.f, 4.f))
792 .finishElements()
793 .expectActual()
794 .state(ClipState::kDeviceRRect)
795 .finishTest());
796
797 // Path
798 run_test_case(r, TestCase::Build("device-path", kDeviceBounds)
799 .actual().intersect().aa()
800 .path(make_octagon(crossesDeviceEdge))
801 .finishElements()
802 .expectActual()
803 .state(ClipState::kComplex)
804 .finishTest());
805}

◆ DEF_TEST() [32/32]

DEF_TEST ( ClipStack_SimpleApply  ,
 
)

Definition at line 1961 of file GrClipStackTest.cpp.

1961 {
1962 using ClipStack = skgpu::ganesh::ClipStack;
1964
1966 std::unique_ptr<SurfaceDrawContext> sdc = SurfaceDrawContext::Make(
1968 SkBackingFit::kExact, kDeviceBounds.size(), SkSurfaceProps(),
1969 /*label=*/{});
1970
1971 ClipStack cs(kDeviceBounds, &SkMatrix::I(), false);
1972
1973 // Offscreen draw is kClippedOut
1974 {
1975 SkRect drawBounds = {-15.f, -15.f, -1.f, -1.f};
1976
1977 GrAppliedClip out(kDeviceBounds.size());
1978 GrClip::Effect effect = cs.apply(context.get(), sdc.get(), NoOp::Get(), GrAAType::kCoverage,
1979 &out, &drawBounds);
1980 REPORTER_ASSERT(r, effect == GrClip::Effect::kClippedOut, "Offscreen draw is clipped out");
1981 }
1982
1983 // Draw contained in clip is kUnclipped
1984 {
1985 SkRect drawBounds = {15.4f, 16.3f, 26.f, 32.f};
1986 cs.save();
1987 cs.clipPath(SkMatrix::I(), make_octagon(drawBounds.makeOutset(5.f, 5.f), 5.f, 5.f),
1989
1990 GrAppliedClip out(kDeviceBounds.size());
1991 GrClip::Effect effect = cs.apply(context.get(), sdc.get(), NoOp::Get(), GrAAType::kCoverage,
1992 &out, &drawBounds);
1993 REPORTER_ASSERT(r, effect == GrClip::Effect::kUnclipped, "Draw inside clip is unclipped");
1994 cs.restore();
1995 }
1996
1997 // Draw bounds are cropped to device space before checking contains
1998 {
1999 SkRect clipRect = {kDeviceBounds.fRight - 20.f, 10.f, kDeviceBounds.fRight, 20.f};
2000 SkRect drawRect = clipRect.makeOffset(10.f, 0.f);
2001
2002 cs.save();
2004
2005 GrAppliedClip out(kDeviceBounds.size());
2006 GrClip::Effect effect = cs.apply(context.get(), sdc.get(), NoOp::Get(), GrAAType::kCoverage,
2007 &out, &drawRect);
2008 REPORTER_ASSERT(r, SkRect::Make(kDeviceBounds).contains(drawRect),
2009 "Draw rect should be clipped to device rect");
2011 "After device clipping, this should be detected as contained within clip");
2012 cs.restore();
2013 }
2014
2015 // Non-AA device rect intersect is just a scissor
2016 {
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();
2020
2021 cs.save();
2023
2024 GrAppliedClip out(kDeviceBounds.size());
2025 GrClip::Effect effect = cs.apply(context.get(), sdc.get(), NoOp::Get(), GrAAType::kCoverage,
2026 &out, &drawRect);
2027 REPORTER_ASSERT(r, effect == GrClip::Effect::kClipped, "Draw should be clipped by rect");
2028 REPORTER_ASSERT(r, !out.hasCoverageFragmentProcessor(), "Clip should not use coverage FPs");
2029 REPORTER_ASSERT(r, !out.hardClip().hasStencilClip(), "Clip should not need stencil");
2030 REPORTER_ASSERT(r, !out.hardClip().windowRectsState().enabled(),
2031 "Clip should not need window rects");
2032 REPORTER_ASSERT(r, out.scissorState().enabled() &&
2033 out.scissorState().rect() == expectedScissor,
2034 "Clip has unexpected scissor rectangle");
2035 cs.restore();
2036 }
2037
2038 // Analytic coverage FPs
2039 auto testHasCoverageFP = [&](SkRect drawBounds) {
2040 GrAppliedClip out(kDeviceBounds.size());
2041 GrClip::Effect effect = cs.apply(context.get(), sdc.get(), NoOp::Get(), GrAAType::kCoverage,
2042 &out, &drawBounds);
2043 REPORTER_ASSERT(r, effect == GrClip::Effect::kClipped, "Draw should be clipped");
2044 REPORTER_ASSERT(r, out.scissorState().enabled(), "Coverage FPs should still set scissor");
2045 REPORTER_ASSERT(r, out.hasCoverageFragmentProcessor(), "Clip should use coverage FP");
2046 };
2047
2048 // Axis-aligned rect can be an analytic FP
2049 {
2050 cs.save();
2051 cs.clipRect(SkMatrix::I(), {10.2f, 8.342f, 63.f, 23.3f}, GrAA::kYes,
2053 testHasCoverageFP({9.f, 10.f, 30.f, 18.f});
2054 cs.restore();
2055 }
2056
2057 // Axis-aligned round rect can be an analytic FP
2058 {
2059 SkRect rect = {4.f, 8.f, 20.f, 20.f};
2060 cs.save();
2061 cs.clipRRect(SkMatrix::I(), SkRRect::MakeRectXY(rect, 3.f, 3.f), GrAA::kYes,
2063 testHasCoverageFP(rect.makeOffset(2.f, 2.f));
2064 cs.restore();
2065 }
2066
2067 // Transformed rect can be an analytic FP
2068 {
2069 SkRect rect = {14.f, 8.f, 30.f, 22.34f};
2070 SkMatrix rot = SkMatrix::RotateDeg(34.f);
2071 cs.save();
2072 cs.clipRect(rot, rect, GrAA::kNo, SkClipOp::kIntersect);
2073 testHasCoverageFP(rot.mapRect(rect));
2074 cs.restore();
2075 }
2076
2077 // Convex polygons can be an analytic FP
2078 {
2079 SkRect rect = {15.f, 15.f, 45.f, 45.f};
2080 cs.save();
2081 cs.clipPath(SkMatrix::I(), make_octagon(rect), GrAA::kYes, SkClipOp::kIntersect);
2082 testHasCoverageFP(rect.makeOutset(2.f, 2.f));
2083 cs.restore();
2084 }
2085}
clipRect(r.rect, r.opAA.op(), r.opAA.aa())) template<> void Draw
constexpr bool contains(std::string_view str, std::string_view needle)
Definition: SkStringView.h:41

◆ disable_tessellation_atlas()

static void disable_tessellation_atlas ( GrContextOptions options)
static

Definition at line 2088 of file GrClipStackTest.cpp.

2088 {
2089 options->fGpuPathRenderers = GpuPathRenderers::kNone;
2090 options->fAvoidStencilBuffers = true;
2091}