1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2016 Google Inc. 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci#include "include/core/SkCanvas.h" 9cb93a386Sopenharmony_ci#include "include/core/SkPath.h" 10cb93a386Sopenharmony_ci#include "include/core/SkSurface.h" 11cb93a386Sopenharmony_ci#include "include/effects/SkDashPathEffect.h" 12cb93a386Sopenharmony_ci#include "include/pathops/SkPathOps.h" 13cb93a386Sopenharmony_ci#include "src/core/SkPathEffectBase.h" 14cb93a386Sopenharmony_ci#include "src/core/SkRectPriv.h" 15cb93a386Sopenharmony_ci#include "src/gpu/geometry/GrStyledShape.h" 16cb93a386Sopenharmony_ci#include "tests/Test.h" 17cb93a386Sopenharmony_ci 18cb93a386Sopenharmony_ci#include <initializer_list> 19cb93a386Sopenharmony_ci#include <functional> 20cb93a386Sopenharmony_ci#include <memory> 21cb93a386Sopenharmony_ci#include <utility> 22cb93a386Sopenharmony_ci 23cb93a386Sopenharmony_ciuint32_t GrStyledShape::testingOnly_getOriginalGenerationID() const { 24cb93a386Sopenharmony_ci if (const auto* lp = this->originalPathForListeners()) { 25cb93a386Sopenharmony_ci return lp->getGenerationID(); 26cb93a386Sopenharmony_ci } 27cb93a386Sopenharmony_ci return SkPath().getGenerationID(); 28cb93a386Sopenharmony_ci} 29cb93a386Sopenharmony_ci 30cb93a386Sopenharmony_cibool GrStyledShape::testingOnly_isPath() const { 31cb93a386Sopenharmony_ci return fShape.isPath(); 32cb93a386Sopenharmony_ci} 33cb93a386Sopenharmony_ci 34cb93a386Sopenharmony_cibool GrStyledShape::testingOnly_isNonVolatilePath() const { 35cb93a386Sopenharmony_ci return fShape.isPath() && !fShape.path().isVolatile(); 36cb93a386Sopenharmony_ci} 37cb93a386Sopenharmony_ci 38cb93a386Sopenharmony_ciusing Key = SkTArray<uint32_t>; 39cb93a386Sopenharmony_ci 40cb93a386Sopenharmony_cistatic bool make_key(Key* key, const GrStyledShape& shape) { 41cb93a386Sopenharmony_ci int size = shape.unstyledKeySize(); 42cb93a386Sopenharmony_ci if (size <= 0) { 43cb93a386Sopenharmony_ci key->reset(0); 44cb93a386Sopenharmony_ci return false; 45cb93a386Sopenharmony_ci } 46cb93a386Sopenharmony_ci SkASSERT(size); 47cb93a386Sopenharmony_ci key->reset(size); 48cb93a386Sopenharmony_ci shape.writeUnstyledKey(key->begin()); 49cb93a386Sopenharmony_ci return true; 50cb93a386Sopenharmony_ci} 51cb93a386Sopenharmony_ci 52cb93a386Sopenharmony_cistatic bool paths_fill_same(const SkPath& a, const SkPath& b) { 53cb93a386Sopenharmony_ci SkPath pathXor; 54cb93a386Sopenharmony_ci Op(a, b, SkPathOp::kXOR_SkPathOp, &pathXor); 55cb93a386Sopenharmony_ci return pathXor.isEmpty(); 56cb93a386Sopenharmony_ci} 57cb93a386Sopenharmony_ci 58cb93a386Sopenharmony_cistatic bool test_bounds_by_rasterizing(const SkPath& path, const SkRect& bounds) { 59cb93a386Sopenharmony_ci // We test the bounds by rasterizing the path into a kRes by kRes grid. The bounds is 60cb93a386Sopenharmony_ci // mapped to the range kRes/4 to 3*kRes/4 in x and y. A difference clip is used to avoid 61cb93a386Sopenharmony_ci // rendering within the bounds (with a tolerance). Then we render the path and check that 62cb93a386Sopenharmony_ci // everything got clipped out. 63cb93a386Sopenharmony_ci static constexpr int kRes = 2000; 64cb93a386Sopenharmony_ci // This tolerance is in units of 1/kRes fractions of the bounds width/height. 65cb93a386Sopenharmony_ci static constexpr int kTol = 2; 66cb93a386Sopenharmony_ci static_assert(kRes % 4 == 0); 67cb93a386Sopenharmony_ci SkImageInfo info = SkImageInfo::MakeA8(kRes, kRes); 68cb93a386Sopenharmony_ci sk_sp<SkSurface> surface = SkSurface::MakeRaster(info); 69cb93a386Sopenharmony_ci surface->getCanvas()->clear(0x0); 70cb93a386Sopenharmony_ci SkRect clip = SkRect::MakeXYWH(kRes/4, kRes/4, kRes/2, kRes/2); 71cb93a386Sopenharmony_ci SkMatrix matrix = SkMatrix::RectToRect(bounds, clip); 72cb93a386Sopenharmony_ci clip.outset(SkIntToScalar(kTol), SkIntToScalar(kTol)); 73cb93a386Sopenharmony_ci surface->getCanvas()->clipRect(clip, SkClipOp::kDifference); 74cb93a386Sopenharmony_ci surface->getCanvas()->concat(matrix); 75cb93a386Sopenharmony_ci SkPaint whitePaint; 76cb93a386Sopenharmony_ci whitePaint.setColor(SK_ColorWHITE); 77cb93a386Sopenharmony_ci surface->getCanvas()->drawPath(path, whitePaint); 78cb93a386Sopenharmony_ci SkPixmap pixmap; 79cb93a386Sopenharmony_ci surface->getCanvas()->peekPixels(&pixmap); 80cb93a386Sopenharmony_ci#if defined(SK_BUILD_FOR_WIN) 81cb93a386Sopenharmony_ci // The static constexpr version in #else causes cl.exe to crash. 82cb93a386Sopenharmony_ci const uint8_t* kZeros = reinterpret_cast<uint8_t*>(calloc(kRes, 1)); 83cb93a386Sopenharmony_ci#else 84cb93a386Sopenharmony_ci static constexpr uint8_t kZeros[kRes] = {0}; 85cb93a386Sopenharmony_ci#endif 86cb93a386Sopenharmony_ci for (int y = 0; y < kRes; ++y) { 87cb93a386Sopenharmony_ci const uint8_t* row = pixmap.addr8(0, y); 88cb93a386Sopenharmony_ci if (0 != memcmp(kZeros, row, kRes)) { 89cb93a386Sopenharmony_ci return false; 90cb93a386Sopenharmony_ci } 91cb93a386Sopenharmony_ci } 92cb93a386Sopenharmony_ci#ifdef SK_BUILD_FOR_WIN 93cb93a386Sopenharmony_ci free(const_cast<uint8_t*>(kZeros)); 94cb93a386Sopenharmony_ci#endif 95cb93a386Sopenharmony_ci return true; 96cb93a386Sopenharmony_ci} 97cb93a386Sopenharmony_ci 98cb93a386Sopenharmony_cistatic bool can_interchange_winding_and_even_odd_fill(const GrStyledShape& shape) { 99cb93a386Sopenharmony_ci SkPath path; 100cb93a386Sopenharmony_ci shape.asPath(&path); 101cb93a386Sopenharmony_ci if (shape.style().hasNonDashPathEffect()) { 102cb93a386Sopenharmony_ci return false; 103cb93a386Sopenharmony_ci } 104cb93a386Sopenharmony_ci const SkStrokeRec::Style strokeRecStyle = shape.style().strokeRec().getStyle(); 105cb93a386Sopenharmony_ci return strokeRecStyle == SkStrokeRec::kStroke_Style || 106cb93a386Sopenharmony_ci strokeRecStyle == SkStrokeRec::kHairline_Style || 107cb93a386Sopenharmony_ci (shape.style().isSimpleFill() && path.isConvex()); 108cb93a386Sopenharmony_ci} 109cb93a386Sopenharmony_ci 110cb93a386Sopenharmony_cistatic void check_equivalence(skiatest::Reporter* r, const GrStyledShape& a, const GrStyledShape& b, 111cb93a386Sopenharmony_ci const Key& keyA, const Key& keyB) { 112cb93a386Sopenharmony_ci // GrStyledShape only respects the input winding direction and start point for rrect shapes 113cb93a386Sopenharmony_ci // when there is a path effect. Thus, if there are two GrStyledShapes representing the same 114cb93a386Sopenharmony_ci // rrect but one has a path effect in its style and the other doesn't then asPath() and the 115cb93a386Sopenharmony_ci // unstyled key will differ. GrStyledShape will have canonicalized the direction and start point 116cb93a386Sopenharmony_ci // for the shape without the path effect. If *both* have path effects then they should have both 117cb93a386Sopenharmony_ci // preserved the direction and starting point. 118cb93a386Sopenharmony_ci 119cb93a386Sopenharmony_ci // The asRRect() output params are all initialized just to silence compiler warnings about 120cb93a386Sopenharmony_ci // uninitialized variables. 121cb93a386Sopenharmony_ci SkRRect rrectA = SkRRect::MakeEmpty(), rrectB = SkRRect::MakeEmpty(); 122cb93a386Sopenharmony_ci SkPathDirection dirA = SkPathDirection::kCW, dirB = SkPathDirection::kCW; 123cb93a386Sopenharmony_ci unsigned startA = ~0U, startB = ~0U; 124cb93a386Sopenharmony_ci bool invertedA = true, invertedB = true; 125cb93a386Sopenharmony_ci 126cb93a386Sopenharmony_ci bool aIsRRect = a.asRRect(&rrectA, &dirA, &startA, &invertedA); 127cb93a386Sopenharmony_ci bool bIsRRect = b.asRRect(&rrectB, &dirB, &startB, &invertedB); 128cb93a386Sopenharmony_ci bool aHasPE = a.style().hasPathEffect(); 129cb93a386Sopenharmony_ci bool bHasPE = b.style().hasPathEffect(); 130cb93a386Sopenharmony_ci bool allowSameRRectButDiffStartAndDir = (aIsRRect && bIsRRect) && (aHasPE != bHasPE); 131cb93a386Sopenharmony_ci // GrStyledShape will close paths with simple fill style. 132cb93a386Sopenharmony_ci bool allowedClosednessDiff = (a.style().isSimpleFill() != b.style().isSimpleFill()); 133cb93a386Sopenharmony_ci SkPath pathA, pathB; 134cb93a386Sopenharmony_ci a.asPath(&pathA); 135cb93a386Sopenharmony_ci b.asPath(&pathB); 136cb93a386Sopenharmony_ci 137cb93a386Sopenharmony_ci // Having a dash path effect can allow 'a' but not 'b' to turn a inverse fill type into a 138cb93a386Sopenharmony_ci // non-inverse fill type (or vice versa). 139cb93a386Sopenharmony_ci bool ignoreInversenessDifference = false; 140cb93a386Sopenharmony_ci if (pathA.isInverseFillType() != pathB.isInverseFillType()) { 141cb93a386Sopenharmony_ci const GrStyledShape* s1 = pathA.isInverseFillType() ? &a : &b; 142cb93a386Sopenharmony_ci const GrStyledShape* s2 = pathA.isInverseFillType() ? &b : &a; 143cb93a386Sopenharmony_ci bool canDropInverse1 = s1->style().isDashed(); 144cb93a386Sopenharmony_ci bool canDropInverse2 = s2->style().isDashed(); 145cb93a386Sopenharmony_ci ignoreInversenessDifference = (canDropInverse1 != canDropInverse2); 146cb93a386Sopenharmony_ci } 147cb93a386Sopenharmony_ci bool ignoreWindingVsEvenOdd = false; 148cb93a386Sopenharmony_ci if (SkPathFillType_ConvertToNonInverse(pathA.getFillType()) != 149cb93a386Sopenharmony_ci SkPathFillType_ConvertToNonInverse(pathB.getFillType())) { 150cb93a386Sopenharmony_ci bool aCanChange = can_interchange_winding_and_even_odd_fill(a); 151cb93a386Sopenharmony_ci bool bCanChange = can_interchange_winding_and_even_odd_fill(b); 152cb93a386Sopenharmony_ci if (aCanChange != bCanChange) { 153cb93a386Sopenharmony_ci ignoreWindingVsEvenOdd = true; 154cb93a386Sopenharmony_ci } 155cb93a386Sopenharmony_ci } 156cb93a386Sopenharmony_ci if (allowSameRRectButDiffStartAndDir) { 157cb93a386Sopenharmony_ci REPORTER_ASSERT(r, rrectA == rrectB); 158cb93a386Sopenharmony_ci REPORTER_ASSERT(r, paths_fill_same(pathA, pathB)); 159cb93a386Sopenharmony_ci REPORTER_ASSERT(r, ignoreInversenessDifference || invertedA == invertedB); 160cb93a386Sopenharmony_ci } else { 161cb93a386Sopenharmony_ci SkPath pA = pathA; 162cb93a386Sopenharmony_ci SkPath pB = pathB; 163cb93a386Sopenharmony_ci REPORTER_ASSERT(r, a.inverseFilled() == pA.isInverseFillType()); 164cb93a386Sopenharmony_ci REPORTER_ASSERT(r, b.inverseFilled() == pB.isInverseFillType()); 165cb93a386Sopenharmony_ci if (ignoreInversenessDifference) { 166cb93a386Sopenharmony_ci pA.setFillType(SkPathFillType_ConvertToNonInverse(pathA.getFillType())); 167cb93a386Sopenharmony_ci pB.setFillType(SkPathFillType_ConvertToNonInverse(pathB.getFillType())); 168cb93a386Sopenharmony_ci } 169cb93a386Sopenharmony_ci if (ignoreWindingVsEvenOdd) { 170cb93a386Sopenharmony_ci pA.setFillType(pA.isInverseFillType() ? SkPathFillType::kInverseEvenOdd 171cb93a386Sopenharmony_ci : SkPathFillType::kEvenOdd); 172cb93a386Sopenharmony_ci pB.setFillType(pB.isInverseFillType() ? SkPathFillType::kInverseEvenOdd 173cb93a386Sopenharmony_ci : SkPathFillType::kEvenOdd); 174cb93a386Sopenharmony_ci } 175cb93a386Sopenharmony_ci if (!ignoreInversenessDifference && !ignoreWindingVsEvenOdd) { 176cb93a386Sopenharmony_ci REPORTER_ASSERT(r, keyA == keyB); 177cb93a386Sopenharmony_ci } else { 178cb93a386Sopenharmony_ci REPORTER_ASSERT(r, keyA != keyB); 179cb93a386Sopenharmony_ci } 180cb93a386Sopenharmony_ci if (allowedClosednessDiff) { 181cb93a386Sopenharmony_ci // GrStyledShape will close paths with simple fill style. Make the non-filled path 182cb93a386Sopenharmony_ci // closed so that the comparision will succeed. Make sure both are closed before 183cb93a386Sopenharmony_ci // comparing. 184cb93a386Sopenharmony_ci pA.close(); 185cb93a386Sopenharmony_ci pB.close(); 186cb93a386Sopenharmony_ci } 187cb93a386Sopenharmony_ci REPORTER_ASSERT(r, pA == pB); 188cb93a386Sopenharmony_ci REPORTER_ASSERT(r, aIsRRect == bIsRRect); 189cb93a386Sopenharmony_ci if (aIsRRect) { 190cb93a386Sopenharmony_ci REPORTER_ASSERT(r, rrectA == rrectB); 191cb93a386Sopenharmony_ci REPORTER_ASSERT(r, dirA == dirB); 192cb93a386Sopenharmony_ci REPORTER_ASSERT(r, startA == startB); 193cb93a386Sopenharmony_ci REPORTER_ASSERT(r, ignoreInversenessDifference || invertedA == invertedB); 194cb93a386Sopenharmony_ci } 195cb93a386Sopenharmony_ci } 196cb93a386Sopenharmony_ci REPORTER_ASSERT(r, a.isEmpty() == b.isEmpty()); 197cb93a386Sopenharmony_ci REPORTER_ASSERT(r, allowedClosednessDiff || a.knownToBeClosed() == b.knownToBeClosed()); 198cb93a386Sopenharmony_ci // closedness can affect convexity. 199cb93a386Sopenharmony_ci REPORTER_ASSERT(r, allowedClosednessDiff || a.knownToBeConvex() == b.knownToBeConvex()); 200cb93a386Sopenharmony_ci if (a.knownToBeConvex()) { 201cb93a386Sopenharmony_ci REPORTER_ASSERT(r, pathA.isConvex()); 202cb93a386Sopenharmony_ci } 203cb93a386Sopenharmony_ci if (b.knownToBeConvex()) { 204cb93a386Sopenharmony_ci REPORTER_ASSERT(r, pathB.isConvex()); 205cb93a386Sopenharmony_ci } 206cb93a386Sopenharmony_ci REPORTER_ASSERT(r, a.bounds() == b.bounds()); 207cb93a386Sopenharmony_ci REPORTER_ASSERT(r, a.segmentMask() == b.segmentMask()); 208cb93a386Sopenharmony_ci // Init these to suppress warnings. 209cb93a386Sopenharmony_ci SkPoint pts[4] {{0, 0,}, {0, 0}, {0, 0}, {0, 0}} ; 210cb93a386Sopenharmony_ci bool invertedLine[2] {true, true}; 211cb93a386Sopenharmony_ci REPORTER_ASSERT(r, a.asLine(pts, &invertedLine[0]) == b.asLine(pts + 2, &invertedLine[1])); 212cb93a386Sopenharmony_ci // mayBeInverseFilledAfterStyling() is allowed to differ if one has a arbitrary PE and the other 213cb93a386Sopenharmony_ci // doesn't (since the PE can set any fill type on its output path). 214cb93a386Sopenharmony_ci // Moreover, dash style explicitly ignores inverseness. So if one is dashed but not the other 215cb93a386Sopenharmony_ci // then they may disagree about inverseness. 216cb93a386Sopenharmony_ci if (a.style().hasNonDashPathEffect() == b.style().hasNonDashPathEffect() && 217cb93a386Sopenharmony_ci a.style().isDashed() == b.style().isDashed()) { 218cb93a386Sopenharmony_ci REPORTER_ASSERT(r, a.mayBeInverseFilledAfterStyling() == 219cb93a386Sopenharmony_ci b.mayBeInverseFilledAfterStyling()); 220cb93a386Sopenharmony_ci } 221cb93a386Sopenharmony_ci if (a.asLine(nullptr, nullptr)) { 222cb93a386Sopenharmony_ci REPORTER_ASSERT(r, pts[2] == pts[0] && pts[3] == pts[1]); 223cb93a386Sopenharmony_ci REPORTER_ASSERT(r, ignoreInversenessDifference || invertedLine[0] == invertedLine[1]); 224cb93a386Sopenharmony_ci REPORTER_ASSERT(r, invertedLine[0] == a.inverseFilled()); 225cb93a386Sopenharmony_ci REPORTER_ASSERT(r, invertedLine[1] == b.inverseFilled()); 226cb93a386Sopenharmony_ci } 227cb93a386Sopenharmony_ci REPORTER_ASSERT(r, ignoreInversenessDifference || a.inverseFilled() == b.inverseFilled()); 228cb93a386Sopenharmony_ci} 229cb93a386Sopenharmony_ci 230cb93a386Sopenharmony_cistatic void check_original_path_ids(skiatest::Reporter* r, const GrStyledShape& base, 231cb93a386Sopenharmony_ci const GrStyledShape& pe, const GrStyledShape& peStroke, 232cb93a386Sopenharmony_ci const GrStyledShape& full) { 233cb93a386Sopenharmony_ci bool baseIsNonVolatilePath = base.testingOnly_isNonVolatilePath(); 234cb93a386Sopenharmony_ci bool peIsPath = pe.testingOnly_isPath(); 235cb93a386Sopenharmony_ci bool peStrokeIsPath = peStroke.testingOnly_isPath(); 236cb93a386Sopenharmony_ci bool fullIsPath = full.testingOnly_isPath(); 237cb93a386Sopenharmony_ci 238cb93a386Sopenharmony_ci REPORTER_ASSERT(r, peStrokeIsPath == fullIsPath); 239cb93a386Sopenharmony_ci 240cb93a386Sopenharmony_ci uint32_t baseID = base.testingOnly_getOriginalGenerationID(); 241cb93a386Sopenharmony_ci uint32_t peID = pe.testingOnly_getOriginalGenerationID(); 242cb93a386Sopenharmony_ci uint32_t peStrokeID = peStroke.testingOnly_getOriginalGenerationID(); 243cb93a386Sopenharmony_ci uint32_t fullID = full.testingOnly_getOriginalGenerationID(); 244cb93a386Sopenharmony_ci 245cb93a386Sopenharmony_ci // All empty paths have the same gen ID 246cb93a386Sopenharmony_ci uint32_t emptyID = SkPath().getGenerationID(); 247cb93a386Sopenharmony_ci 248cb93a386Sopenharmony_ci // If we started with a real path, then our genID should match that path's gen ID (and not be 249cb93a386Sopenharmony_ci // empty). If we started with a simple shape or a volatile path, our original path should have 250cb93a386Sopenharmony_ci // been reset. 251cb93a386Sopenharmony_ci REPORTER_ASSERT(r, baseIsNonVolatilePath == (baseID != emptyID)); 252cb93a386Sopenharmony_ci 253cb93a386Sopenharmony_ci // For the derived shapes, if they're simple types, their original paths should have been reset 254cb93a386Sopenharmony_ci REPORTER_ASSERT(r, peIsPath || (peID == emptyID)); 255cb93a386Sopenharmony_ci REPORTER_ASSERT(r, peStrokeIsPath || (peStrokeID == emptyID)); 256cb93a386Sopenharmony_ci REPORTER_ASSERT(r, fullIsPath || (fullID == emptyID)); 257cb93a386Sopenharmony_ci 258cb93a386Sopenharmony_ci if (!peIsPath) { 259cb93a386Sopenharmony_ci // If the path effect produces a simple shape, then there are no unbroken chains to test 260cb93a386Sopenharmony_ci return; 261cb93a386Sopenharmony_ci } 262cb93a386Sopenharmony_ci 263cb93a386Sopenharmony_ci // From here on, we know that the path effect produced a shape that was a "real" path 264cb93a386Sopenharmony_ci 265cb93a386Sopenharmony_ci if (baseIsNonVolatilePath) { 266cb93a386Sopenharmony_ci REPORTER_ASSERT(r, baseID == peID); 267cb93a386Sopenharmony_ci } 268cb93a386Sopenharmony_ci 269cb93a386Sopenharmony_ci if (peStrokeIsPath) { 270cb93a386Sopenharmony_ci REPORTER_ASSERT(r, peID == peStrokeID); 271cb93a386Sopenharmony_ci REPORTER_ASSERT(r, peStrokeID == fullID); 272cb93a386Sopenharmony_ci } 273cb93a386Sopenharmony_ci 274cb93a386Sopenharmony_ci if (baseIsNonVolatilePath && peStrokeIsPath) { 275cb93a386Sopenharmony_ci REPORTER_ASSERT(r, baseID == peStrokeID); 276cb93a386Sopenharmony_ci REPORTER_ASSERT(r, baseID == fullID); 277cb93a386Sopenharmony_ci } 278cb93a386Sopenharmony_ci} 279cb93a386Sopenharmony_ci 280cb93a386Sopenharmony_civoid test_inversions(skiatest::Reporter* r, const GrStyledShape& shape, const Key& shapeKey) { 281cb93a386Sopenharmony_ci GrStyledShape preserve = GrStyledShape::MakeFilled( 282cb93a386Sopenharmony_ci shape, GrStyledShape::FillInversion::kPreserve); 283cb93a386Sopenharmony_ci Key preserveKey; 284cb93a386Sopenharmony_ci make_key(&preserveKey, preserve); 285cb93a386Sopenharmony_ci 286cb93a386Sopenharmony_ci GrStyledShape flip = GrStyledShape::MakeFilled(shape, GrStyledShape::FillInversion::kFlip); 287cb93a386Sopenharmony_ci Key flipKey; 288cb93a386Sopenharmony_ci make_key(&flipKey, flip); 289cb93a386Sopenharmony_ci 290cb93a386Sopenharmony_ci GrStyledShape inverted = GrStyledShape::MakeFilled( 291cb93a386Sopenharmony_ci shape, GrStyledShape::FillInversion::kForceInverted); 292cb93a386Sopenharmony_ci Key invertedKey; 293cb93a386Sopenharmony_ci make_key(&invertedKey, inverted); 294cb93a386Sopenharmony_ci 295cb93a386Sopenharmony_ci GrStyledShape noninverted = GrStyledShape::MakeFilled( 296cb93a386Sopenharmony_ci shape, GrStyledShape::FillInversion::kForceNoninverted); 297cb93a386Sopenharmony_ci Key noninvertedKey; 298cb93a386Sopenharmony_ci make_key(&noninvertedKey, noninverted); 299cb93a386Sopenharmony_ci 300cb93a386Sopenharmony_ci if (invertedKey.count() || noninvertedKey.count()) { 301cb93a386Sopenharmony_ci REPORTER_ASSERT(r, invertedKey != noninvertedKey); 302cb93a386Sopenharmony_ci } 303cb93a386Sopenharmony_ci if (shape.style().isSimpleFill()) { 304cb93a386Sopenharmony_ci check_equivalence(r, shape, preserve, shapeKey, preserveKey); 305cb93a386Sopenharmony_ci } 306cb93a386Sopenharmony_ci if (shape.inverseFilled()) { 307cb93a386Sopenharmony_ci check_equivalence(r, preserve, inverted, preserveKey, invertedKey); 308cb93a386Sopenharmony_ci check_equivalence(r, flip, noninverted, flipKey, noninvertedKey); 309cb93a386Sopenharmony_ci } else { 310cb93a386Sopenharmony_ci check_equivalence(r, preserve, noninverted, preserveKey, noninvertedKey); 311cb93a386Sopenharmony_ci check_equivalence(r, flip, inverted, flipKey, invertedKey); 312cb93a386Sopenharmony_ci } 313cb93a386Sopenharmony_ci 314cb93a386Sopenharmony_ci GrStyledShape doubleFlip = GrStyledShape::MakeFilled(flip, GrStyledShape::FillInversion::kFlip); 315cb93a386Sopenharmony_ci Key doubleFlipKey; 316cb93a386Sopenharmony_ci make_key(&doubleFlipKey, doubleFlip); 317cb93a386Sopenharmony_ci // It can be the case that the double flip has no key but preserve does. This happens when the 318cb93a386Sopenharmony_ci // original shape has an inherited style key. That gets dropped on the first inversion flip. 319cb93a386Sopenharmony_ci if (preserveKey.count() && !doubleFlipKey.count()) { 320cb93a386Sopenharmony_ci preserveKey.reset(); 321cb93a386Sopenharmony_ci } 322cb93a386Sopenharmony_ci check_equivalence(r, preserve, doubleFlip, preserveKey, doubleFlipKey); 323cb93a386Sopenharmony_ci} 324cb93a386Sopenharmony_ci 325cb93a386Sopenharmony_cinamespace { 326cb93a386Sopenharmony_ci/** 327cb93a386Sopenharmony_ci * Geo is a factory for creating a GrStyledShape from another representation. It also answers some 328cb93a386Sopenharmony_ci * questions about expected behavior for GrStyledShape given the inputs. 329cb93a386Sopenharmony_ci */ 330cb93a386Sopenharmony_ciclass Geo { 331cb93a386Sopenharmony_cipublic: 332cb93a386Sopenharmony_ci virtual ~Geo() {} 333cb93a386Sopenharmony_ci virtual GrStyledShape makeShape(const SkPaint&) const = 0; 334cb93a386Sopenharmony_ci virtual SkPath path() const = 0; 335cb93a386Sopenharmony_ci // These functions allow tests to check for special cases where style gets 336cb93a386Sopenharmony_ci // applied by GrStyledShape in its constructor (without calling GrStyledShape::applyStyle). 337cb93a386Sopenharmony_ci // These unfortunately rely on knowing details of GrStyledShape's implementation. 338cb93a386Sopenharmony_ci // These predicates are factored out here to avoid littering the rest of the 339cb93a386Sopenharmony_ci // test code with GrStyledShape implementation details. 340cb93a386Sopenharmony_ci virtual bool fillChangesGeom() const { return false; } 341cb93a386Sopenharmony_ci virtual bool strokeIsConvertedToFill() const { return false; } 342cb93a386Sopenharmony_ci virtual bool strokeAndFillIsConvertedToFill(const SkPaint&) const { return false; } 343cb93a386Sopenharmony_ci // Is this something we expect GrStyledShape to recognize as something simpler than a path. 344cb93a386Sopenharmony_ci virtual bool isNonPath(const SkPaint& paint) const { return true; } 345cb93a386Sopenharmony_ci}; 346cb93a386Sopenharmony_ci 347cb93a386Sopenharmony_ciclass RectGeo : public Geo { 348cb93a386Sopenharmony_cipublic: 349cb93a386Sopenharmony_ci RectGeo(const SkRect& rect) : fRect(rect) {} 350cb93a386Sopenharmony_ci 351cb93a386Sopenharmony_ci SkPath path() const override { 352cb93a386Sopenharmony_ci SkPath path; 353cb93a386Sopenharmony_ci path.addRect(fRect); 354cb93a386Sopenharmony_ci return path; 355cb93a386Sopenharmony_ci } 356cb93a386Sopenharmony_ci 357cb93a386Sopenharmony_ci GrStyledShape makeShape(const SkPaint& paint) const override { 358cb93a386Sopenharmony_ci return GrStyledShape(fRect, paint); 359cb93a386Sopenharmony_ci } 360cb93a386Sopenharmony_ci 361cb93a386Sopenharmony_ci bool strokeAndFillIsConvertedToFill(const SkPaint& paint) const override { 362cb93a386Sopenharmony_ci SkASSERT(paint.getStyle() == SkPaint::kStrokeAndFill_Style); 363cb93a386Sopenharmony_ci // Converted to an outset rectangle or round rect 364cb93a386Sopenharmony_ci return (paint.getStrokeJoin() == SkPaint::kMiter_Join && 365cb93a386Sopenharmony_ci paint.getStrokeMiter() >= SK_ScalarSqrt2) || 366cb93a386Sopenharmony_ci paint.getStrokeJoin() == SkPaint::kRound_Join; 367cb93a386Sopenharmony_ci } 368cb93a386Sopenharmony_ci 369cb93a386Sopenharmony_ciprivate: 370cb93a386Sopenharmony_ci SkRect fRect; 371cb93a386Sopenharmony_ci}; 372cb93a386Sopenharmony_ci 373cb93a386Sopenharmony_ciclass RRectGeo : public Geo { 374cb93a386Sopenharmony_cipublic: 375cb93a386Sopenharmony_ci RRectGeo(const SkRRect& rrect) : fRRect(rrect) {} 376cb93a386Sopenharmony_ci 377cb93a386Sopenharmony_ci GrStyledShape makeShape(const SkPaint& paint) const override { 378cb93a386Sopenharmony_ci return GrStyledShape(fRRect, paint); 379cb93a386Sopenharmony_ci } 380cb93a386Sopenharmony_ci 381cb93a386Sopenharmony_ci SkPath path() const override { 382cb93a386Sopenharmony_ci SkPath path; 383cb93a386Sopenharmony_ci path.addRRect(fRRect); 384cb93a386Sopenharmony_ci return path; 385cb93a386Sopenharmony_ci } 386cb93a386Sopenharmony_ci 387cb93a386Sopenharmony_ci bool strokeAndFillIsConvertedToFill(const SkPaint& paint) const override { 388cb93a386Sopenharmony_ci SkASSERT(paint.getStyle() == SkPaint::kStrokeAndFill_Style); 389cb93a386Sopenharmony_ci if (fRRect.isRect()) { 390cb93a386Sopenharmony_ci return RectGeo(fRRect.rect()).strokeAndFillIsConvertedToFill(paint); 391cb93a386Sopenharmony_ci } 392cb93a386Sopenharmony_ci return false; 393cb93a386Sopenharmony_ci } 394cb93a386Sopenharmony_ci 395cb93a386Sopenharmony_ciprivate: 396cb93a386Sopenharmony_ci SkRRect fRRect; 397cb93a386Sopenharmony_ci}; 398cb93a386Sopenharmony_ci 399cb93a386Sopenharmony_ciclass ArcGeo : public Geo { 400cb93a386Sopenharmony_cipublic: 401cb93a386Sopenharmony_ci ArcGeo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, bool useCenter) 402cb93a386Sopenharmony_ci : fOval(oval) 403cb93a386Sopenharmony_ci , fStartAngle(startAngle) 404cb93a386Sopenharmony_ci , fSweepAngle(sweepAngle) 405cb93a386Sopenharmony_ci , fUseCenter(useCenter) {} 406cb93a386Sopenharmony_ci 407cb93a386Sopenharmony_ci SkPath path() const override { 408cb93a386Sopenharmony_ci SkPath path; 409cb93a386Sopenharmony_ci SkPathPriv::CreateDrawArcPath(&path, fOval, fStartAngle, fSweepAngle, fUseCenter, false); 410cb93a386Sopenharmony_ci return path; 411cb93a386Sopenharmony_ci } 412cb93a386Sopenharmony_ci 413cb93a386Sopenharmony_ci GrStyledShape makeShape(const SkPaint& paint) const override { 414cb93a386Sopenharmony_ci return GrStyledShape::MakeArc(fOval, fStartAngle, fSweepAngle, fUseCenter, GrStyle(paint)); 415cb93a386Sopenharmony_ci } 416cb93a386Sopenharmony_ci 417cb93a386Sopenharmony_ci // GrStyledShape specializes when created from arc params but it doesn't recognize arcs from 418cb93a386Sopenharmony_ci // SkPath. 419cb93a386Sopenharmony_ci bool isNonPath(const SkPaint& paint) const override { return false; } 420cb93a386Sopenharmony_ci 421cb93a386Sopenharmony_ciprivate: 422cb93a386Sopenharmony_ci SkRect fOval; 423cb93a386Sopenharmony_ci SkScalar fStartAngle; 424cb93a386Sopenharmony_ci SkScalar fSweepAngle; 425cb93a386Sopenharmony_ci bool fUseCenter; 426cb93a386Sopenharmony_ci}; 427cb93a386Sopenharmony_ci 428cb93a386Sopenharmony_ciclass PathGeo : public Geo { 429cb93a386Sopenharmony_cipublic: 430cb93a386Sopenharmony_ci enum class Invert { kNo, kYes }; 431cb93a386Sopenharmony_ci 432cb93a386Sopenharmony_ci PathGeo(const SkPath& path, Invert invert) : fPath(path) { 433cb93a386Sopenharmony_ci SkASSERT(!path.isInverseFillType()); 434cb93a386Sopenharmony_ci if (Invert::kYes == invert) { 435cb93a386Sopenharmony_ci if (fPath.getFillType() == SkPathFillType::kEvenOdd) { 436cb93a386Sopenharmony_ci fPath.setFillType(SkPathFillType::kInverseEvenOdd); 437cb93a386Sopenharmony_ci } else { 438cb93a386Sopenharmony_ci SkASSERT(fPath.getFillType() == SkPathFillType::kWinding); 439cb93a386Sopenharmony_ci fPath.setFillType(SkPathFillType::kInverseWinding); 440cb93a386Sopenharmony_ci } 441cb93a386Sopenharmony_ci } 442cb93a386Sopenharmony_ci } 443cb93a386Sopenharmony_ci 444cb93a386Sopenharmony_ci GrStyledShape makeShape(const SkPaint& paint) const override { 445cb93a386Sopenharmony_ci return GrStyledShape(fPath, paint); 446cb93a386Sopenharmony_ci } 447cb93a386Sopenharmony_ci 448cb93a386Sopenharmony_ci SkPath path() const override { return fPath; } 449cb93a386Sopenharmony_ci 450cb93a386Sopenharmony_ci bool fillChangesGeom() const override { 451cb93a386Sopenharmony_ci // unclosed rects get closed. Lines get turned into empty geometry 452cb93a386Sopenharmony_ci return this->isUnclosedRect() || fPath.isLine(nullptr); 453cb93a386Sopenharmony_ci } 454cb93a386Sopenharmony_ci 455cb93a386Sopenharmony_ci bool strokeIsConvertedToFill() const override { 456cb93a386Sopenharmony_ci return this->isAxisAlignedLine(); 457cb93a386Sopenharmony_ci } 458cb93a386Sopenharmony_ci 459cb93a386Sopenharmony_ci bool strokeAndFillIsConvertedToFill(const SkPaint& paint) const override { 460cb93a386Sopenharmony_ci SkASSERT(paint.getStyle() == SkPaint::kStrokeAndFill_Style); 461cb93a386Sopenharmony_ci if (this->isAxisAlignedLine()) { 462cb93a386Sopenharmony_ci // The fill is ignored (zero area) and the stroke is converted to a rrect. 463cb93a386Sopenharmony_ci return true; 464cb93a386Sopenharmony_ci } 465cb93a386Sopenharmony_ci SkRect rect; 466cb93a386Sopenharmony_ci unsigned start; 467cb93a386Sopenharmony_ci SkPathDirection dir; 468cb93a386Sopenharmony_ci if (SkPathPriv::IsSimpleRect(fPath, false, &rect, &dir, &start)) { 469cb93a386Sopenharmony_ci return RectGeo(rect).strokeAndFillIsConvertedToFill(paint); 470cb93a386Sopenharmony_ci } 471cb93a386Sopenharmony_ci return false; 472cb93a386Sopenharmony_ci } 473cb93a386Sopenharmony_ci 474cb93a386Sopenharmony_ci bool isNonPath(const SkPaint& paint) const override { 475cb93a386Sopenharmony_ci return fPath.isLine(nullptr) || fPath.isEmpty(); 476cb93a386Sopenharmony_ci } 477cb93a386Sopenharmony_ci 478cb93a386Sopenharmony_ciprivate: 479cb93a386Sopenharmony_ci bool isAxisAlignedLine() const { 480cb93a386Sopenharmony_ci SkPoint pts[2]; 481cb93a386Sopenharmony_ci if (!fPath.isLine(pts)) { 482cb93a386Sopenharmony_ci return false; 483cb93a386Sopenharmony_ci } 484cb93a386Sopenharmony_ci return pts[0].fX == pts[1].fX || pts[0].fY == pts[1].fY; 485cb93a386Sopenharmony_ci } 486cb93a386Sopenharmony_ci 487cb93a386Sopenharmony_ci bool isUnclosedRect() const { 488cb93a386Sopenharmony_ci bool closed; 489cb93a386Sopenharmony_ci return fPath.isRect(nullptr, &closed, nullptr) && !closed; 490cb93a386Sopenharmony_ci } 491cb93a386Sopenharmony_ci 492cb93a386Sopenharmony_ci SkPath fPath; 493cb93a386Sopenharmony_ci}; 494cb93a386Sopenharmony_ci 495cb93a386Sopenharmony_ciclass RRectPathGeo : public PathGeo { 496cb93a386Sopenharmony_cipublic: 497cb93a386Sopenharmony_ci enum class RRectForStroke { kNo, kYes }; 498cb93a386Sopenharmony_ci 499cb93a386Sopenharmony_ci RRectPathGeo(const SkPath& path, const SkRRect& equivalentRRect, RRectForStroke rrectForStroke, 500cb93a386Sopenharmony_ci Invert invert) 501cb93a386Sopenharmony_ci : PathGeo(path, invert) 502cb93a386Sopenharmony_ci , fRRect(equivalentRRect) 503cb93a386Sopenharmony_ci , fRRectForStroke(rrectForStroke) {} 504cb93a386Sopenharmony_ci 505cb93a386Sopenharmony_ci RRectPathGeo(const SkPath& path, const SkRect& equivalentRect, RRectForStroke rrectForStroke, 506cb93a386Sopenharmony_ci Invert invert) 507cb93a386Sopenharmony_ci : RRectPathGeo(path, SkRRect::MakeRect(equivalentRect), rrectForStroke, invert) {} 508cb93a386Sopenharmony_ci 509cb93a386Sopenharmony_ci bool isNonPath(const SkPaint& paint) const override { 510cb93a386Sopenharmony_ci if (SkPaint::kFill_Style == paint.getStyle() || RRectForStroke::kYes == fRRectForStroke) { 511cb93a386Sopenharmony_ci return true; 512cb93a386Sopenharmony_ci } 513cb93a386Sopenharmony_ci return false; 514cb93a386Sopenharmony_ci } 515cb93a386Sopenharmony_ci 516cb93a386Sopenharmony_ci const SkRRect& rrect() const { return fRRect; } 517cb93a386Sopenharmony_ci 518cb93a386Sopenharmony_ciprivate: 519cb93a386Sopenharmony_ci SkRRect fRRect; 520cb93a386Sopenharmony_ci RRectForStroke fRRectForStroke; 521cb93a386Sopenharmony_ci}; 522cb93a386Sopenharmony_ci 523cb93a386Sopenharmony_ciclass TestCase { 524cb93a386Sopenharmony_cipublic: 525cb93a386Sopenharmony_ci TestCase(const Geo& geo, const SkPaint& paint, skiatest::Reporter* r, 526cb93a386Sopenharmony_ci SkScalar scale = SK_Scalar1) 527cb93a386Sopenharmony_ci : fBase(new GrStyledShape(geo.makeShape(paint))) { 528cb93a386Sopenharmony_ci this->init(r, scale); 529cb93a386Sopenharmony_ci } 530cb93a386Sopenharmony_ci 531cb93a386Sopenharmony_ci template <typename... ShapeArgs> 532cb93a386Sopenharmony_ci TestCase(skiatest::Reporter* r, ShapeArgs... shapeArgs) 533cb93a386Sopenharmony_ci : fBase(new GrStyledShape(shapeArgs...)) { 534cb93a386Sopenharmony_ci this->init(r, SK_Scalar1); 535cb93a386Sopenharmony_ci } 536cb93a386Sopenharmony_ci 537cb93a386Sopenharmony_ci TestCase(const GrStyledShape& shape, skiatest::Reporter* r, SkScalar scale = SK_Scalar1) 538cb93a386Sopenharmony_ci : fBase(new GrStyledShape(shape)) { 539cb93a386Sopenharmony_ci this->init(r, scale); 540cb93a386Sopenharmony_ci } 541cb93a386Sopenharmony_ci 542cb93a386Sopenharmony_ci struct SelfExpectations { 543cb93a386Sopenharmony_ci bool fPEHasEffect; 544cb93a386Sopenharmony_ci bool fPEHasValidKey; 545cb93a386Sopenharmony_ci bool fStrokeApplies; 546cb93a386Sopenharmony_ci }; 547cb93a386Sopenharmony_ci 548cb93a386Sopenharmony_ci void testExpectations(skiatest::Reporter* reporter, SelfExpectations expectations) const; 549cb93a386Sopenharmony_ci 550cb93a386Sopenharmony_ci enum ComparisonExpecation { 551cb93a386Sopenharmony_ci kAllDifferent_ComparisonExpecation, 552cb93a386Sopenharmony_ci kSameUpToPE_ComparisonExpecation, 553cb93a386Sopenharmony_ci kSameUpToStroke_ComparisonExpecation, 554cb93a386Sopenharmony_ci kAllSame_ComparisonExpecation, 555cb93a386Sopenharmony_ci }; 556cb93a386Sopenharmony_ci 557cb93a386Sopenharmony_ci void compare(skiatest::Reporter*, const TestCase& that, ComparisonExpecation) const; 558cb93a386Sopenharmony_ci 559cb93a386Sopenharmony_ci const GrStyledShape& baseShape() const { return *fBase; } 560cb93a386Sopenharmony_ci const GrStyledShape& appliedPathEffectShape() const { return *fAppliedPE; } 561cb93a386Sopenharmony_ci const GrStyledShape& appliedFullStyleShape() const { return *fAppliedFull; } 562cb93a386Sopenharmony_ci 563cb93a386Sopenharmony_ci // The returned array's count will be 0 if the key shape has no key. 564cb93a386Sopenharmony_ci const Key& baseKey() const { return fBaseKey; } 565cb93a386Sopenharmony_ci const Key& appliedPathEffectKey() const { return fAppliedPEKey; } 566cb93a386Sopenharmony_ci const Key& appliedFullStyleKey() const { return fAppliedFullKey; } 567cb93a386Sopenharmony_ci const Key& appliedPathEffectThenStrokeKey() const { return fAppliedPEThenStrokeKey; } 568cb93a386Sopenharmony_ci 569cb93a386Sopenharmony_ciprivate: 570cb93a386Sopenharmony_ci static void CheckBounds(skiatest::Reporter* r, const GrStyledShape& shape, 571cb93a386Sopenharmony_ci const SkRect& bounds) { 572cb93a386Sopenharmony_ci SkPath path; 573cb93a386Sopenharmony_ci shape.asPath(&path); 574cb93a386Sopenharmony_ci // If the bounds are empty, the path ought to be as well. 575cb93a386Sopenharmony_ci if (bounds.fLeft > bounds.fRight || bounds.fTop > bounds.fBottom) { 576cb93a386Sopenharmony_ci REPORTER_ASSERT(r, path.isEmpty()); 577cb93a386Sopenharmony_ci return; 578cb93a386Sopenharmony_ci } 579cb93a386Sopenharmony_ci if (path.isEmpty()) { 580cb93a386Sopenharmony_ci return; 581cb93a386Sopenharmony_ci } 582cb93a386Sopenharmony_ci // The bounds API explicitly calls out that it does not consider inverseness. 583cb93a386Sopenharmony_ci SkPath p = path; 584cb93a386Sopenharmony_ci p.setFillType(SkPathFillType_ConvertToNonInverse(path.getFillType())); 585cb93a386Sopenharmony_ci REPORTER_ASSERT(r, test_bounds_by_rasterizing(p, bounds)); 586cb93a386Sopenharmony_ci } 587cb93a386Sopenharmony_ci 588cb93a386Sopenharmony_ci void init(skiatest::Reporter* r, SkScalar scale) { 589cb93a386Sopenharmony_ci fAppliedPE = std::make_unique<GrStyledShape>(); 590cb93a386Sopenharmony_ci fAppliedPEThenStroke = std::make_unique<GrStyledShape>(); 591cb93a386Sopenharmony_ci fAppliedFull = std::make_unique<GrStyledShape>(); 592cb93a386Sopenharmony_ci 593cb93a386Sopenharmony_ci *fAppliedPE = fBase->applyStyle(GrStyle::Apply::kPathEffectOnly, scale); 594cb93a386Sopenharmony_ci *fAppliedPEThenStroke = 595cb93a386Sopenharmony_ci fAppliedPE->applyStyle(GrStyle::Apply::kPathEffectAndStrokeRec, scale); 596cb93a386Sopenharmony_ci *fAppliedFull = fBase->applyStyle(GrStyle::Apply::kPathEffectAndStrokeRec, scale); 597cb93a386Sopenharmony_ci 598cb93a386Sopenharmony_ci make_key(&fBaseKey, *fBase); 599cb93a386Sopenharmony_ci make_key(&fAppliedPEKey, *fAppliedPE); 600cb93a386Sopenharmony_ci make_key(&fAppliedPEThenStrokeKey, *fAppliedPEThenStroke); 601cb93a386Sopenharmony_ci make_key(&fAppliedFullKey, *fAppliedFull); 602cb93a386Sopenharmony_ci 603cb93a386Sopenharmony_ci // All shapes should report the same "original" path, so that path renderers can get to it 604cb93a386Sopenharmony_ci // if necessary. 605cb93a386Sopenharmony_ci check_original_path_ids(r, *fBase, *fAppliedPE, *fAppliedPEThenStroke, *fAppliedFull); 606cb93a386Sopenharmony_ci 607cb93a386Sopenharmony_ci // Applying the path effect and then the stroke should always be the same as applying 608cb93a386Sopenharmony_ci // both in one go. 609cb93a386Sopenharmony_ci REPORTER_ASSERT(r, fAppliedPEThenStrokeKey == fAppliedFullKey); 610cb93a386Sopenharmony_ci SkPath a, b; 611cb93a386Sopenharmony_ci fAppliedPEThenStroke->asPath(&a); 612cb93a386Sopenharmony_ci fAppliedFull->asPath(&b); 613cb93a386Sopenharmony_ci // If the output of the path effect is a rrect then it is possible for a and b to be 614cb93a386Sopenharmony_ci // different paths that fill identically. The reason is that fAppliedFull will do this: 615cb93a386Sopenharmony_ci // base -> apply path effect -> rrect_as_path -> stroke -> stroked_rrect_as_path 616cb93a386Sopenharmony_ci // fAppliedPEThenStroke will have converted the rrect_as_path back to a rrect. However, 617cb93a386Sopenharmony_ci // now that there is no longer a path effect, the direction and starting index get 618cb93a386Sopenharmony_ci // canonicalized before the stroke. 619cb93a386Sopenharmony_ci if (fAppliedPE->asRRect(nullptr, nullptr, nullptr, nullptr)) { 620cb93a386Sopenharmony_ci REPORTER_ASSERT(r, paths_fill_same(a, b)); 621cb93a386Sopenharmony_ci } else { 622cb93a386Sopenharmony_ci REPORTER_ASSERT(r, a == b); 623cb93a386Sopenharmony_ci } 624cb93a386Sopenharmony_ci REPORTER_ASSERT(r, fAppliedFull->isEmpty() == fAppliedPEThenStroke->isEmpty()); 625cb93a386Sopenharmony_ci 626cb93a386Sopenharmony_ci SkPath path; 627cb93a386Sopenharmony_ci fBase->asPath(&path); 628cb93a386Sopenharmony_ci REPORTER_ASSERT(r, path.isEmpty() == fBase->isEmpty()); 629cb93a386Sopenharmony_ci REPORTER_ASSERT(r, path.getSegmentMasks() == fBase->segmentMask()); 630cb93a386Sopenharmony_ci fAppliedPE->asPath(&path); 631cb93a386Sopenharmony_ci REPORTER_ASSERT(r, path.isEmpty() == fAppliedPE->isEmpty()); 632cb93a386Sopenharmony_ci REPORTER_ASSERT(r, path.getSegmentMasks() == fAppliedPE->segmentMask()); 633cb93a386Sopenharmony_ci fAppliedFull->asPath(&path); 634cb93a386Sopenharmony_ci REPORTER_ASSERT(r, path.isEmpty() == fAppliedFull->isEmpty()); 635cb93a386Sopenharmony_ci REPORTER_ASSERT(r, path.getSegmentMasks() == fAppliedFull->segmentMask()); 636cb93a386Sopenharmony_ci 637cb93a386Sopenharmony_ci CheckBounds(r, *fBase, fBase->bounds()); 638cb93a386Sopenharmony_ci CheckBounds(r, *fAppliedPE, fAppliedPE->bounds()); 639cb93a386Sopenharmony_ci CheckBounds(r, *fAppliedPEThenStroke, fAppliedPEThenStroke->bounds()); 640cb93a386Sopenharmony_ci CheckBounds(r, *fAppliedFull, fAppliedFull->bounds()); 641cb93a386Sopenharmony_ci SkRect styledBounds = fBase->styledBounds(); 642cb93a386Sopenharmony_ci CheckBounds(r, *fAppliedFull, styledBounds); 643cb93a386Sopenharmony_ci styledBounds = fAppliedPE->styledBounds(); 644cb93a386Sopenharmony_ci CheckBounds(r, *fAppliedFull, styledBounds); 645cb93a386Sopenharmony_ci 646cb93a386Sopenharmony_ci // Check that the same path is produced when style is applied by GrStyledShape and GrStyle. 647cb93a386Sopenharmony_ci SkPath preStyle; 648cb93a386Sopenharmony_ci SkPath postPathEffect; 649cb93a386Sopenharmony_ci SkPath postAllStyle; 650cb93a386Sopenharmony_ci 651cb93a386Sopenharmony_ci fBase->asPath(&preStyle); 652cb93a386Sopenharmony_ci SkStrokeRec postPEStrokeRec(SkStrokeRec::kFill_InitStyle); 653cb93a386Sopenharmony_ci if (fBase->style().applyPathEffectToPath(&postPathEffect, &postPEStrokeRec, preStyle, 654cb93a386Sopenharmony_ci scale)) { 655cb93a386Sopenharmony_ci // run postPathEffect through GrStyledShape to get any geometry reductions that would 656cb93a386Sopenharmony_ci // have occurred to fAppliedPE. 657cb93a386Sopenharmony_ci GrStyledShape(postPathEffect, GrStyle(postPEStrokeRec, nullptr)) 658cb93a386Sopenharmony_ci .asPath(&postPathEffect); 659cb93a386Sopenharmony_ci 660cb93a386Sopenharmony_ci SkPath testPath; 661cb93a386Sopenharmony_ci fAppliedPE->asPath(&testPath); 662cb93a386Sopenharmony_ci REPORTER_ASSERT(r, testPath == postPathEffect); 663cb93a386Sopenharmony_ci REPORTER_ASSERT(r, postPEStrokeRec.hasEqualEffect(fAppliedPE->style().strokeRec())); 664cb93a386Sopenharmony_ci } 665cb93a386Sopenharmony_ci SkStrokeRec::InitStyle fillOrHairline; 666cb93a386Sopenharmony_ci if (fBase->style().applyToPath(&postAllStyle, &fillOrHairline, preStyle, scale)) { 667cb93a386Sopenharmony_ci SkPath testPath; 668cb93a386Sopenharmony_ci fAppliedFull->asPath(&testPath); 669cb93a386Sopenharmony_ci if (fBase->style().hasPathEffect()) { 670cb93a386Sopenharmony_ci // Because GrStyledShape always does two-stage application when there is a path 671cb93a386Sopenharmony_ci // effect there may be a reduction/canonicalization step between the path effect and 672cb93a386Sopenharmony_ci // strokerec not reflected in postAllStyle since it applied both the path effect 673cb93a386Sopenharmony_ci // and strokerec without analyzing the intermediate path. 674cb93a386Sopenharmony_ci REPORTER_ASSERT(r, paths_fill_same(postAllStyle, testPath)); 675cb93a386Sopenharmony_ci } else { 676cb93a386Sopenharmony_ci // Make sure that postAllStyle sees any reductions/canonicalizations that 677cb93a386Sopenharmony_ci // GrStyledShape would apply. 678cb93a386Sopenharmony_ci GrStyledShape(postAllStyle, GrStyle(fillOrHairline)).asPath(&postAllStyle); 679cb93a386Sopenharmony_ci REPORTER_ASSERT(r, testPath == postAllStyle); 680cb93a386Sopenharmony_ci } 681cb93a386Sopenharmony_ci 682cb93a386Sopenharmony_ci if (fillOrHairline == SkStrokeRec::kFill_InitStyle) { 683cb93a386Sopenharmony_ci REPORTER_ASSERT(r, fAppliedFull->style().isSimpleFill()); 684cb93a386Sopenharmony_ci } else { 685cb93a386Sopenharmony_ci REPORTER_ASSERT(r, fAppliedFull->style().isSimpleHairline()); 686cb93a386Sopenharmony_ci } 687cb93a386Sopenharmony_ci } 688cb93a386Sopenharmony_ci test_inversions(r, *fBase, fBaseKey); 689cb93a386Sopenharmony_ci test_inversions(r, *fAppliedPE, fAppliedPEKey); 690cb93a386Sopenharmony_ci test_inversions(r, *fAppliedFull, fAppliedFullKey); 691cb93a386Sopenharmony_ci } 692cb93a386Sopenharmony_ci 693cb93a386Sopenharmony_ci std::unique_ptr<GrStyledShape> fBase; 694cb93a386Sopenharmony_ci std::unique_ptr<GrStyledShape> fAppliedPE; 695cb93a386Sopenharmony_ci std::unique_ptr<GrStyledShape> fAppliedPEThenStroke; 696cb93a386Sopenharmony_ci std::unique_ptr<GrStyledShape> fAppliedFull; 697cb93a386Sopenharmony_ci 698cb93a386Sopenharmony_ci Key fBaseKey; 699cb93a386Sopenharmony_ci Key fAppliedPEKey; 700cb93a386Sopenharmony_ci Key fAppliedPEThenStrokeKey; 701cb93a386Sopenharmony_ci Key fAppliedFullKey; 702cb93a386Sopenharmony_ci}; 703cb93a386Sopenharmony_ci 704cb93a386Sopenharmony_civoid TestCase::testExpectations(skiatest::Reporter* reporter, SelfExpectations expectations) const { 705cb93a386Sopenharmony_ci // The base's key should always be valid (unless the path is volatile) 706cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, fBaseKey.count()); 707cb93a386Sopenharmony_ci if (expectations.fPEHasEffect) { 708cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, fBaseKey != fAppliedPEKey); 709cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, expectations.fPEHasValidKey == SkToBool(fAppliedPEKey.count())); 710cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, fBaseKey != fAppliedFullKey); 711cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, expectations.fPEHasValidKey == SkToBool(fAppliedFullKey.count())); 712cb93a386Sopenharmony_ci if (expectations.fStrokeApplies && expectations.fPEHasValidKey) { 713cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, fAppliedPEKey != fAppliedFullKey); 714cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, SkToBool(fAppliedFullKey.count())); 715cb93a386Sopenharmony_ci } 716cb93a386Sopenharmony_ci } else { 717cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, fBaseKey == fAppliedPEKey); 718cb93a386Sopenharmony_ci SkPath a, b; 719cb93a386Sopenharmony_ci fBase->asPath(&a); 720cb93a386Sopenharmony_ci fAppliedPE->asPath(&b); 721cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, a == b); 722cb93a386Sopenharmony_ci if (expectations.fStrokeApplies) { 723cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, fBaseKey != fAppliedFullKey); 724cb93a386Sopenharmony_ci } else { 725cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, fBaseKey == fAppliedFullKey); 726cb93a386Sopenharmony_ci } 727cb93a386Sopenharmony_ci } 728cb93a386Sopenharmony_ci} 729cb93a386Sopenharmony_ci 730cb93a386Sopenharmony_civoid TestCase::compare(skiatest::Reporter* r, const TestCase& that, 731cb93a386Sopenharmony_ci ComparisonExpecation expectation) const { 732cb93a386Sopenharmony_ci SkPath a, b; 733cb93a386Sopenharmony_ci switch (expectation) { 734cb93a386Sopenharmony_ci case kAllDifferent_ComparisonExpecation: 735cb93a386Sopenharmony_ci REPORTER_ASSERT(r, fBaseKey != that.fBaseKey); 736cb93a386Sopenharmony_ci REPORTER_ASSERT(r, fAppliedPEKey != that.fAppliedPEKey); 737cb93a386Sopenharmony_ci REPORTER_ASSERT(r, fAppliedFullKey != that.fAppliedFullKey); 738cb93a386Sopenharmony_ci break; 739cb93a386Sopenharmony_ci case kSameUpToPE_ComparisonExpecation: 740cb93a386Sopenharmony_ci check_equivalence(r, *fBase, *that.fBase, fBaseKey, that.fBaseKey); 741cb93a386Sopenharmony_ci REPORTER_ASSERT(r, fAppliedPEKey != that.fAppliedPEKey); 742cb93a386Sopenharmony_ci REPORTER_ASSERT(r, fAppliedFullKey != that.fAppliedFullKey); 743cb93a386Sopenharmony_ci break; 744cb93a386Sopenharmony_ci case kSameUpToStroke_ComparisonExpecation: 745cb93a386Sopenharmony_ci check_equivalence(r, *fBase, *that.fBase, fBaseKey, that.fBaseKey); 746cb93a386Sopenharmony_ci check_equivalence(r, *fAppliedPE, *that.fAppliedPE, fAppliedPEKey, that.fAppliedPEKey); 747cb93a386Sopenharmony_ci REPORTER_ASSERT(r, fAppliedFullKey != that.fAppliedFullKey); 748cb93a386Sopenharmony_ci break; 749cb93a386Sopenharmony_ci case kAllSame_ComparisonExpecation: 750cb93a386Sopenharmony_ci check_equivalence(r, *fBase, *that.fBase, fBaseKey, that.fBaseKey); 751cb93a386Sopenharmony_ci check_equivalence(r, *fAppliedPE, *that.fAppliedPE, fAppliedPEKey, that.fAppliedPEKey); 752cb93a386Sopenharmony_ci check_equivalence(r, *fAppliedFull, *that.fAppliedFull, fAppliedFullKey, 753cb93a386Sopenharmony_ci that.fAppliedFullKey); 754cb93a386Sopenharmony_ci break; 755cb93a386Sopenharmony_ci } 756cb93a386Sopenharmony_ci} 757cb93a386Sopenharmony_ci} // namespace 758cb93a386Sopenharmony_ci 759cb93a386Sopenharmony_cistatic sk_sp<SkPathEffect> make_dash() { 760cb93a386Sopenharmony_ci static const SkScalar kIntervals[] = { 0.25, 3.f, 0.5, 2.f }; 761cb93a386Sopenharmony_ci static const SkScalar kPhase = 0.75; 762cb93a386Sopenharmony_ci return SkDashPathEffect::Make(kIntervals, SK_ARRAY_COUNT(kIntervals), kPhase); 763cb93a386Sopenharmony_ci} 764cb93a386Sopenharmony_ci 765cb93a386Sopenharmony_cistatic sk_sp<SkPathEffect> make_null_dash() { 766cb93a386Sopenharmony_ci static const SkScalar kNullIntervals[] = {0, 0, 0, 0, 0, 0}; 767cb93a386Sopenharmony_ci return SkDashPathEffect::Make(kNullIntervals, SK_ARRAY_COUNT(kNullIntervals), 0.f); 768cb93a386Sopenharmony_ci} 769cb93a386Sopenharmony_ci 770cb93a386Sopenharmony_ci// We make enough TestCases, and they're large enough, that on Google3 builds we exceed 771cb93a386Sopenharmony_ci// the maximum stack frame limit. make_TestCase() moves those temporaries over to the heap. 772cb93a386Sopenharmony_citemplate <typename... Args> 773cb93a386Sopenharmony_cistatic std::unique_ptr<TestCase> make_TestCase(Args&&... args) { 774cb93a386Sopenharmony_ci return std::make_unique<TestCase>( std::forward<Args>(args)... ); 775cb93a386Sopenharmony_ci} 776cb93a386Sopenharmony_ci 777cb93a386Sopenharmony_cistatic void test_basic(skiatest::Reporter* reporter, const Geo& geo) { 778cb93a386Sopenharmony_ci sk_sp<SkPathEffect> dashPE = make_dash(); 779cb93a386Sopenharmony_ci 780cb93a386Sopenharmony_ci TestCase::SelfExpectations expectations; 781cb93a386Sopenharmony_ci SkPaint fill; 782cb93a386Sopenharmony_ci 783cb93a386Sopenharmony_ci TestCase fillCase(geo, fill, reporter); 784cb93a386Sopenharmony_ci expectations.fPEHasEffect = false; 785cb93a386Sopenharmony_ci expectations.fPEHasValidKey = false; 786cb93a386Sopenharmony_ci expectations.fStrokeApplies = false; 787cb93a386Sopenharmony_ci fillCase.testExpectations(reporter, expectations); 788cb93a386Sopenharmony_ci // Test that another GrStyledShape instance built from the same primitive is the same. 789cb93a386Sopenharmony_ci make_TestCase(geo, fill, reporter) 790cb93a386Sopenharmony_ci ->compare(reporter, fillCase, TestCase::kAllSame_ComparisonExpecation); 791cb93a386Sopenharmony_ci 792cb93a386Sopenharmony_ci SkPaint stroke2RoundBevel; 793cb93a386Sopenharmony_ci stroke2RoundBevel.setStyle(SkPaint::kStroke_Style); 794cb93a386Sopenharmony_ci stroke2RoundBevel.setStrokeCap(SkPaint::kRound_Cap); 795cb93a386Sopenharmony_ci stroke2RoundBevel.setStrokeJoin(SkPaint::kBevel_Join); 796cb93a386Sopenharmony_ci stroke2RoundBevel.setStrokeWidth(2.f); 797cb93a386Sopenharmony_ci TestCase stroke2RoundBevelCase(geo, stroke2RoundBevel, reporter); 798cb93a386Sopenharmony_ci expectations.fPEHasValidKey = true; 799cb93a386Sopenharmony_ci expectations.fPEHasEffect = false; 800cb93a386Sopenharmony_ci expectations.fStrokeApplies = !geo.strokeIsConvertedToFill(); 801cb93a386Sopenharmony_ci stroke2RoundBevelCase.testExpectations(reporter, expectations); 802cb93a386Sopenharmony_ci make_TestCase(geo, stroke2RoundBevel, reporter) 803cb93a386Sopenharmony_ci ->compare(reporter, stroke2RoundBevelCase, TestCase::kAllSame_ComparisonExpecation); 804cb93a386Sopenharmony_ci 805cb93a386Sopenharmony_ci SkPaint stroke2RoundBevelDash = stroke2RoundBevel; 806cb93a386Sopenharmony_ci stroke2RoundBevelDash.setPathEffect(make_dash()); 807cb93a386Sopenharmony_ci TestCase stroke2RoundBevelDashCase(geo, stroke2RoundBevelDash, reporter); 808cb93a386Sopenharmony_ci expectations.fPEHasValidKey = true; 809cb93a386Sopenharmony_ci expectations.fPEHasEffect = true; 810cb93a386Sopenharmony_ci expectations.fStrokeApplies = true; 811cb93a386Sopenharmony_ci stroke2RoundBevelDashCase.testExpectations(reporter, expectations); 812cb93a386Sopenharmony_ci make_TestCase(geo, stroke2RoundBevelDash, reporter) 813cb93a386Sopenharmony_ci ->compare(reporter, stroke2RoundBevelDashCase, TestCase::kAllSame_ComparisonExpecation); 814cb93a386Sopenharmony_ci 815cb93a386Sopenharmony_ci if (geo.fillChangesGeom() || geo.strokeIsConvertedToFill()) { 816cb93a386Sopenharmony_ci fillCase.compare(reporter, stroke2RoundBevelCase, 817cb93a386Sopenharmony_ci TestCase::kAllDifferent_ComparisonExpecation); 818cb93a386Sopenharmony_ci fillCase.compare(reporter, stroke2RoundBevelDashCase, 819cb93a386Sopenharmony_ci TestCase::kAllDifferent_ComparisonExpecation); 820cb93a386Sopenharmony_ci } else { 821cb93a386Sopenharmony_ci fillCase.compare(reporter, stroke2RoundBevelCase, 822cb93a386Sopenharmony_ci TestCase::kSameUpToStroke_ComparisonExpecation); 823cb93a386Sopenharmony_ci fillCase.compare(reporter, stroke2RoundBevelDashCase, 824cb93a386Sopenharmony_ci TestCase::kSameUpToPE_ComparisonExpecation); 825cb93a386Sopenharmony_ci } 826cb93a386Sopenharmony_ci if (geo.strokeIsConvertedToFill()) { 827cb93a386Sopenharmony_ci stroke2RoundBevelCase.compare(reporter, stroke2RoundBevelDashCase, 828cb93a386Sopenharmony_ci TestCase::kAllDifferent_ComparisonExpecation); 829cb93a386Sopenharmony_ci } else { 830cb93a386Sopenharmony_ci stroke2RoundBevelCase.compare(reporter, stroke2RoundBevelDashCase, 831cb93a386Sopenharmony_ci TestCase::kSameUpToPE_ComparisonExpecation); 832cb93a386Sopenharmony_ci } 833cb93a386Sopenharmony_ci 834cb93a386Sopenharmony_ci // Stroke and fill cases 835cb93a386Sopenharmony_ci SkPaint stroke2RoundBevelAndFill = stroke2RoundBevel; 836cb93a386Sopenharmony_ci stroke2RoundBevelAndFill.setStyle(SkPaint::kStrokeAndFill_Style); 837cb93a386Sopenharmony_ci TestCase stroke2RoundBevelAndFillCase(geo, stroke2RoundBevelAndFill, reporter); 838cb93a386Sopenharmony_ci expectations.fPEHasValidKey = true; 839cb93a386Sopenharmony_ci expectations.fPEHasEffect = false; 840cb93a386Sopenharmony_ci expectations.fStrokeApplies = !geo.strokeIsConvertedToFill(); 841cb93a386Sopenharmony_ci stroke2RoundBevelAndFillCase.testExpectations(reporter, expectations); 842cb93a386Sopenharmony_ci make_TestCase(geo, stroke2RoundBevelAndFill, reporter)->compare( 843cb93a386Sopenharmony_ci reporter, stroke2RoundBevelAndFillCase, TestCase::kAllSame_ComparisonExpecation); 844cb93a386Sopenharmony_ci 845cb93a386Sopenharmony_ci SkPaint stroke2RoundBevelAndFillDash = stroke2RoundBevelDash; 846cb93a386Sopenharmony_ci stroke2RoundBevelAndFillDash.setStyle(SkPaint::kStrokeAndFill_Style); 847cb93a386Sopenharmony_ci TestCase stroke2RoundBevelAndFillDashCase(geo, stroke2RoundBevelAndFillDash, reporter); 848cb93a386Sopenharmony_ci expectations.fPEHasValidKey = true; 849cb93a386Sopenharmony_ci expectations.fPEHasEffect = false; 850cb93a386Sopenharmony_ci expectations.fStrokeApplies = !geo.strokeIsConvertedToFill(); 851cb93a386Sopenharmony_ci stroke2RoundBevelAndFillDashCase.testExpectations(reporter, expectations); 852cb93a386Sopenharmony_ci make_TestCase(geo, stroke2RoundBevelAndFillDash, reporter)->compare( 853cb93a386Sopenharmony_ci reporter, stroke2RoundBevelAndFillDashCase, TestCase::kAllSame_ComparisonExpecation); 854cb93a386Sopenharmony_ci stroke2RoundBevelAndFillDashCase.compare(reporter, stroke2RoundBevelAndFillCase, 855cb93a386Sopenharmony_ci TestCase::kAllSame_ComparisonExpecation); 856cb93a386Sopenharmony_ci 857cb93a386Sopenharmony_ci SkPaint hairline; 858cb93a386Sopenharmony_ci hairline.setStyle(SkPaint::kStroke_Style); 859cb93a386Sopenharmony_ci hairline.setStrokeWidth(0.f); 860cb93a386Sopenharmony_ci TestCase hairlineCase(geo, hairline, reporter); 861cb93a386Sopenharmony_ci // Since hairline style doesn't change the SkPath data, it is keyed identically to fill (except 862cb93a386Sopenharmony_ci // in the line and unclosed rect cases). 863cb93a386Sopenharmony_ci if (geo.fillChangesGeom()) { 864cb93a386Sopenharmony_ci hairlineCase.compare(reporter, fillCase, TestCase::kAllDifferent_ComparisonExpecation); 865cb93a386Sopenharmony_ci } else { 866cb93a386Sopenharmony_ci hairlineCase.compare(reporter, fillCase, TestCase::kAllSame_ComparisonExpecation); 867cb93a386Sopenharmony_ci } 868cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, hairlineCase.baseShape().style().isSimpleHairline()); 869cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, hairlineCase.appliedFullStyleShape().style().isSimpleHairline()); 870cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, hairlineCase.appliedPathEffectShape().style().isSimpleHairline()); 871cb93a386Sopenharmony_ci 872cb93a386Sopenharmony_ci} 873cb93a386Sopenharmony_ci 874cb93a386Sopenharmony_cistatic void test_scale(skiatest::Reporter* reporter, const Geo& geo) { 875cb93a386Sopenharmony_ci sk_sp<SkPathEffect> dashPE = make_dash(); 876cb93a386Sopenharmony_ci 877cb93a386Sopenharmony_ci static const SkScalar kS1 = 1.f; 878cb93a386Sopenharmony_ci static const SkScalar kS2 = 2.f; 879cb93a386Sopenharmony_ci 880cb93a386Sopenharmony_ci SkPaint fill; 881cb93a386Sopenharmony_ci TestCase fillCase1(geo, fill, reporter, kS1); 882cb93a386Sopenharmony_ci TestCase fillCase2(geo, fill, reporter, kS2); 883cb93a386Sopenharmony_ci // Scale doesn't affect fills. 884cb93a386Sopenharmony_ci fillCase1.compare(reporter, fillCase2, TestCase::kAllSame_ComparisonExpecation); 885cb93a386Sopenharmony_ci 886cb93a386Sopenharmony_ci SkPaint hairline; 887cb93a386Sopenharmony_ci hairline.setStyle(SkPaint::kStroke_Style); 888cb93a386Sopenharmony_ci hairline.setStrokeWidth(0.f); 889cb93a386Sopenharmony_ci TestCase hairlineCase1(geo, hairline, reporter, kS1); 890cb93a386Sopenharmony_ci TestCase hairlineCase2(geo, hairline, reporter, kS2); 891cb93a386Sopenharmony_ci // Scale doesn't affect hairlines. 892cb93a386Sopenharmony_ci hairlineCase1.compare(reporter, hairlineCase2, TestCase::kAllSame_ComparisonExpecation); 893cb93a386Sopenharmony_ci 894cb93a386Sopenharmony_ci SkPaint stroke; 895cb93a386Sopenharmony_ci stroke.setStyle(SkPaint::kStroke_Style); 896cb93a386Sopenharmony_ci stroke.setStrokeWidth(2.f); 897cb93a386Sopenharmony_ci TestCase strokeCase1(geo, stroke, reporter, kS1); 898cb93a386Sopenharmony_ci TestCase strokeCase2(geo, stroke, reporter, kS2); 899cb93a386Sopenharmony_ci // Scale affects the stroke 900cb93a386Sopenharmony_ci if (geo.strokeIsConvertedToFill()) { 901cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, !strokeCase1.baseShape().style().applies()); 902cb93a386Sopenharmony_ci strokeCase1.compare(reporter, strokeCase2, TestCase::kAllSame_ComparisonExpecation); 903cb93a386Sopenharmony_ci } else { 904cb93a386Sopenharmony_ci strokeCase1.compare(reporter, strokeCase2, TestCase::kSameUpToStroke_ComparisonExpecation); 905cb93a386Sopenharmony_ci } 906cb93a386Sopenharmony_ci 907cb93a386Sopenharmony_ci SkPaint strokeDash = stroke; 908cb93a386Sopenharmony_ci strokeDash.setPathEffect(make_dash()); 909cb93a386Sopenharmony_ci TestCase strokeDashCase1(geo, strokeDash, reporter, kS1); 910cb93a386Sopenharmony_ci TestCase strokeDashCase2(geo, strokeDash, reporter, kS2); 911cb93a386Sopenharmony_ci // Scale affects the dash and the stroke. 912cb93a386Sopenharmony_ci strokeDashCase1.compare(reporter, strokeDashCase2, 913cb93a386Sopenharmony_ci TestCase::kSameUpToPE_ComparisonExpecation); 914cb93a386Sopenharmony_ci 915cb93a386Sopenharmony_ci // Stroke and fill cases 916cb93a386Sopenharmony_ci SkPaint strokeAndFill = stroke; 917cb93a386Sopenharmony_ci strokeAndFill.setStyle(SkPaint::kStrokeAndFill_Style); 918cb93a386Sopenharmony_ci TestCase strokeAndFillCase1(geo, strokeAndFill, reporter, kS1); 919cb93a386Sopenharmony_ci TestCase strokeAndFillCase2(geo, strokeAndFill, reporter, kS2); 920cb93a386Sopenharmony_ci SkPaint strokeAndFillDash = strokeDash; 921cb93a386Sopenharmony_ci strokeAndFillDash.setStyle(SkPaint::kStrokeAndFill_Style); 922cb93a386Sopenharmony_ci // Dash is ignored for stroke and fill 923cb93a386Sopenharmony_ci TestCase strokeAndFillDashCase1(geo, strokeAndFillDash, reporter, kS1); 924cb93a386Sopenharmony_ci TestCase strokeAndFillDashCase2(geo, strokeAndFillDash, reporter, kS2); 925cb93a386Sopenharmony_ci // Scale affects the stroke, but check to make sure this didn't become a simpler shape (e.g. 926cb93a386Sopenharmony_ci // stroke-and-filled rect can become a rect), in which case the scale shouldn't matter and the 927cb93a386Sopenharmony_ci // geometries should agree. 928cb93a386Sopenharmony_ci if (geo.strokeAndFillIsConvertedToFill(strokeAndFillDash)) { 929cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, !strokeAndFillCase1.baseShape().style().applies()); 930cb93a386Sopenharmony_ci strokeAndFillCase1.compare(reporter, strokeAndFillCase2, 931cb93a386Sopenharmony_ci TestCase::kAllSame_ComparisonExpecation); 932cb93a386Sopenharmony_ci strokeAndFillDashCase1.compare(reporter, strokeAndFillDashCase2, 933cb93a386Sopenharmony_ci TestCase::kAllSame_ComparisonExpecation); 934cb93a386Sopenharmony_ci } else { 935cb93a386Sopenharmony_ci strokeAndFillCase1.compare(reporter, strokeAndFillCase2, 936cb93a386Sopenharmony_ci TestCase::kSameUpToStroke_ComparisonExpecation); 937cb93a386Sopenharmony_ci } 938cb93a386Sopenharmony_ci strokeAndFillDashCase1.compare(reporter, strokeAndFillCase1, 939cb93a386Sopenharmony_ci TestCase::kAllSame_ComparisonExpecation); 940cb93a386Sopenharmony_ci strokeAndFillDashCase2.compare(reporter, strokeAndFillCase2, 941cb93a386Sopenharmony_ci TestCase::kAllSame_ComparisonExpecation); 942cb93a386Sopenharmony_ci} 943cb93a386Sopenharmony_ci 944cb93a386Sopenharmony_citemplate <typename T> 945cb93a386Sopenharmony_cistatic void test_stroke_param_impl(skiatest::Reporter* reporter, const Geo& geo, 946cb93a386Sopenharmony_ci std::function<void(SkPaint*, T)> setter, T a, T b, 947cb93a386Sopenharmony_ci bool paramAffectsStroke, 948cb93a386Sopenharmony_ci bool paramAffectsDashAndStroke) { 949cb93a386Sopenharmony_ci // Set the stroke width so that we don't get hairline. However, call the setter afterward so 950cb93a386Sopenharmony_ci // that it can override the stroke width. 951cb93a386Sopenharmony_ci SkPaint strokeA; 952cb93a386Sopenharmony_ci strokeA.setStyle(SkPaint::kStroke_Style); 953cb93a386Sopenharmony_ci strokeA.setStrokeWidth(2.f); 954cb93a386Sopenharmony_ci setter(&strokeA, a); 955cb93a386Sopenharmony_ci SkPaint strokeB; 956cb93a386Sopenharmony_ci strokeB.setStyle(SkPaint::kStroke_Style); 957cb93a386Sopenharmony_ci strokeB.setStrokeWidth(2.f); 958cb93a386Sopenharmony_ci setter(&strokeB, b); 959cb93a386Sopenharmony_ci 960cb93a386Sopenharmony_ci TestCase strokeACase(geo, strokeA, reporter); 961cb93a386Sopenharmony_ci TestCase strokeBCase(geo, strokeB, reporter); 962cb93a386Sopenharmony_ci if (paramAffectsStroke) { 963cb93a386Sopenharmony_ci // If stroking is immediately incorporated into a geometric transformation then the base 964cb93a386Sopenharmony_ci // shapes will differ. 965cb93a386Sopenharmony_ci if (geo.strokeIsConvertedToFill()) { 966cb93a386Sopenharmony_ci strokeACase.compare(reporter, strokeBCase, 967cb93a386Sopenharmony_ci TestCase::kAllDifferent_ComparisonExpecation); 968cb93a386Sopenharmony_ci } else { 969cb93a386Sopenharmony_ci strokeACase.compare(reporter, strokeBCase, 970cb93a386Sopenharmony_ci TestCase::kSameUpToStroke_ComparisonExpecation); 971cb93a386Sopenharmony_ci } 972cb93a386Sopenharmony_ci } else { 973cb93a386Sopenharmony_ci strokeACase.compare(reporter, strokeBCase, TestCase::kAllSame_ComparisonExpecation); 974cb93a386Sopenharmony_ci } 975cb93a386Sopenharmony_ci 976cb93a386Sopenharmony_ci SkPaint strokeAndFillA = strokeA; 977cb93a386Sopenharmony_ci SkPaint strokeAndFillB = strokeB; 978cb93a386Sopenharmony_ci strokeAndFillA.setStyle(SkPaint::kStrokeAndFill_Style); 979cb93a386Sopenharmony_ci strokeAndFillB.setStyle(SkPaint::kStrokeAndFill_Style); 980cb93a386Sopenharmony_ci TestCase strokeAndFillACase(geo, strokeAndFillA, reporter); 981cb93a386Sopenharmony_ci TestCase strokeAndFillBCase(geo, strokeAndFillB, reporter); 982cb93a386Sopenharmony_ci if (paramAffectsStroke) { 983cb93a386Sopenharmony_ci // If stroking is immediately incorporated into a geometric transformation then the base 984cb93a386Sopenharmony_ci // shapes will differ. 985cb93a386Sopenharmony_ci if (geo.strokeAndFillIsConvertedToFill(strokeAndFillA) || 986cb93a386Sopenharmony_ci geo.strokeAndFillIsConvertedToFill(strokeAndFillB)) { 987cb93a386Sopenharmony_ci strokeAndFillACase.compare(reporter, strokeAndFillBCase, 988cb93a386Sopenharmony_ci TestCase::kAllDifferent_ComparisonExpecation); 989cb93a386Sopenharmony_ci } else { 990cb93a386Sopenharmony_ci strokeAndFillACase.compare(reporter, strokeAndFillBCase, 991cb93a386Sopenharmony_ci TestCase::kSameUpToStroke_ComparisonExpecation); 992cb93a386Sopenharmony_ci } 993cb93a386Sopenharmony_ci } else { 994cb93a386Sopenharmony_ci strokeAndFillACase.compare(reporter, strokeAndFillBCase, 995cb93a386Sopenharmony_ci TestCase::kAllSame_ComparisonExpecation); 996cb93a386Sopenharmony_ci } 997cb93a386Sopenharmony_ci 998cb93a386Sopenharmony_ci // Make sure stroking params don't affect fill style. 999cb93a386Sopenharmony_ci SkPaint fillA = strokeA, fillB = strokeB; 1000cb93a386Sopenharmony_ci fillA.setStyle(SkPaint::kFill_Style); 1001cb93a386Sopenharmony_ci fillB.setStyle(SkPaint::kFill_Style); 1002cb93a386Sopenharmony_ci TestCase fillACase(geo, fillA, reporter); 1003cb93a386Sopenharmony_ci TestCase fillBCase(geo, fillB, reporter); 1004cb93a386Sopenharmony_ci fillACase.compare(reporter, fillBCase, TestCase::kAllSame_ComparisonExpecation); 1005cb93a386Sopenharmony_ci 1006cb93a386Sopenharmony_ci // Make sure just applying the dash but not stroke gives the same key for both stroking 1007cb93a386Sopenharmony_ci // variations. 1008cb93a386Sopenharmony_ci SkPaint dashA = strokeA, dashB = strokeB; 1009cb93a386Sopenharmony_ci dashA.setPathEffect(make_dash()); 1010cb93a386Sopenharmony_ci dashB.setPathEffect(make_dash()); 1011cb93a386Sopenharmony_ci TestCase dashACase(geo, dashA, reporter); 1012cb93a386Sopenharmony_ci TestCase dashBCase(geo, dashB, reporter); 1013cb93a386Sopenharmony_ci if (paramAffectsDashAndStroke) { 1014cb93a386Sopenharmony_ci dashACase.compare(reporter, dashBCase, TestCase::kSameUpToStroke_ComparisonExpecation); 1015cb93a386Sopenharmony_ci } else { 1016cb93a386Sopenharmony_ci dashACase.compare(reporter, dashBCase, TestCase::kAllSame_ComparisonExpecation); 1017cb93a386Sopenharmony_ci } 1018cb93a386Sopenharmony_ci} 1019cb93a386Sopenharmony_ci 1020cb93a386Sopenharmony_citemplate <typename T> 1021cb93a386Sopenharmony_cistatic void test_stroke_param(skiatest::Reporter* reporter, const Geo& geo, 1022cb93a386Sopenharmony_ci std::function<void(SkPaint*, T)> setter, T a, T b) { 1023cb93a386Sopenharmony_ci test_stroke_param_impl(reporter, geo, setter, a, b, true, true); 1024cb93a386Sopenharmony_ci}; 1025cb93a386Sopenharmony_ci 1026cb93a386Sopenharmony_cistatic void test_stroke_cap(skiatest::Reporter* reporter, const Geo& geo) { 1027cb93a386Sopenharmony_ci SkPaint hairline; 1028cb93a386Sopenharmony_ci hairline.setStrokeWidth(0); 1029cb93a386Sopenharmony_ci hairline.setStyle(SkPaint::kStroke_Style); 1030cb93a386Sopenharmony_ci GrStyledShape shape = geo.makeShape(hairline); 1031cb93a386Sopenharmony_ci // The cap should only affect shapes that may be open. 1032cb93a386Sopenharmony_ci bool affectsStroke = !shape.knownToBeClosed(); 1033cb93a386Sopenharmony_ci // Dashing adds ends that need caps. 1034cb93a386Sopenharmony_ci bool affectsDashAndStroke = true; 1035cb93a386Sopenharmony_ci test_stroke_param_impl<SkPaint::Cap>( 1036cb93a386Sopenharmony_ci reporter, 1037cb93a386Sopenharmony_ci geo, 1038cb93a386Sopenharmony_ci [](SkPaint* p, SkPaint::Cap c) { p->setStrokeCap(c);}, 1039cb93a386Sopenharmony_ci SkPaint::kButt_Cap, SkPaint::kRound_Cap, 1040cb93a386Sopenharmony_ci affectsStroke, 1041cb93a386Sopenharmony_ci affectsDashAndStroke); 1042cb93a386Sopenharmony_ci}; 1043cb93a386Sopenharmony_ci 1044cb93a386Sopenharmony_cistatic bool shape_known_not_to_have_joins(const GrStyledShape& shape) { 1045cb93a386Sopenharmony_ci return shape.asLine(nullptr, nullptr) || shape.isEmpty(); 1046cb93a386Sopenharmony_ci} 1047cb93a386Sopenharmony_ci 1048cb93a386Sopenharmony_cistatic void test_stroke_join(skiatest::Reporter* reporter, const Geo& geo) { 1049cb93a386Sopenharmony_ci SkPaint hairline; 1050cb93a386Sopenharmony_ci hairline.setStrokeWidth(0); 1051cb93a386Sopenharmony_ci hairline.setStyle(SkPaint::kStroke_Style); 1052cb93a386Sopenharmony_ci GrStyledShape shape = geo.makeShape(hairline); 1053cb93a386Sopenharmony_ci // GrStyledShape recognizes certain types don't have joins and will prevent the join type from 1054cb93a386Sopenharmony_ci // affecting the style key. 1055cb93a386Sopenharmony_ci // Dashing doesn't add additional joins. However, GrStyledShape currently loses track of this 1056cb93a386Sopenharmony_ci // after applying the dash. 1057cb93a386Sopenharmony_ci bool affectsStroke = !shape_known_not_to_have_joins(shape); 1058cb93a386Sopenharmony_ci test_stroke_param_impl<SkPaint::Join>( 1059cb93a386Sopenharmony_ci reporter, 1060cb93a386Sopenharmony_ci geo, 1061cb93a386Sopenharmony_ci [](SkPaint* p, SkPaint::Join j) { p->setStrokeJoin(j);}, 1062cb93a386Sopenharmony_ci SkPaint::kRound_Join, SkPaint::kBevel_Join, 1063cb93a386Sopenharmony_ci affectsStroke, true); 1064cb93a386Sopenharmony_ci}; 1065cb93a386Sopenharmony_ci 1066cb93a386Sopenharmony_cistatic void test_miter_limit(skiatest::Reporter* reporter, const Geo& geo) { 1067cb93a386Sopenharmony_ci auto setMiterJoinAndLimit = [](SkPaint* p, SkScalar miter) { 1068cb93a386Sopenharmony_ci p->setStrokeJoin(SkPaint::kMiter_Join); 1069cb93a386Sopenharmony_ci p->setStrokeMiter(miter); 1070cb93a386Sopenharmony_ci }; 1071cb93a386Sopenharmony_ci 1072cb93a386Sopenharmony_ci auto setOtherJoinAndLimit = [](SkPaint* p, SkScalar miter) { 1073cb93a386Sopenharmony_ci p->setStrokeJoin(SkPaint::kRound_Join); 1074cb93a386Sopenharmony_ci p->setStrokeMiter(miter); 1075cb93a386Sopenharmony_ci }; 1076cb93a386Sopenharmony_ci 1077cb93a386Sopenharmony_ci SkPaint hairline; 1078cb93a386Sopenharmony_ci hairline.setStrokeWidth(0); 1079cb93a386Sopenharmony_ci hairline.setStyle(SkPaint::kStroke_Style); 1080cb93a386Sopenharmony_ci GrStyledShape shape = geo.makeShape(hairline); 1081cb93a386Sopenharmony_ci bool mayHaveJoins = !shape_known_not_to_have_joins(shape); 1082cb93a386Sopenharmony_ci 1083cb93a386Sopenharmony_ci // The miter limit should affect stroked and dashed-stroked cases when the join type is 1084cb93a386Sopenharmony_ci // miter. 1085cb93a386Sopenharmony_ci test_stroke_param_impl<SkScalar>( 1086cb93a386Sopenharmony_ci reporter, 1087cb93a386Sopenharmony_ci geo, 1088cb93a386Sopenharmony_ci setMiterJoinAndLimit, 1089cb93a386Sopenharmony_ci 0.5f, 0.75f, 1090cb93a386Sopenharmony_ci mayHaveJoins, 1091cb93a386Sopenharmony_ci true); 1092cb93a386Sopenharmony_ci 1093cb93a386Sopenharmony_ci // The miter limit should not affect stroked and dashed-stroked cases when the join type is 1094cb93a386Sopenharmony_ci // not miter. 1095cb93a386Sopenharmony_ci test_stroke_param_impl<SkScalar>( 1096cb93a386Sopenharmony_ci reporter, 1097cb93a386Sopenharmony_ci geo, 1098cb93a386Sopenharmony_ci setOtherJoinAndLimit, 1099cb93a386Sopenharmony_ci 0.5f, 0.75f, 1100cb93a386Sopenharmony_ci false, 1101cb93a386Sopenharmony_ci false); 1102cb93a386Sopenharmony_ci} 1103cb93a386Sopenharmony_ci 1104cb93a386Sopenharmony_cistatic void test_dash_fill(skiatest::Reporter* reporter, const Geo& geo) { 1105cb93a386Sopenharmony_ci // A dash with no stroke should have no effect 1106cb93a386Sopenharmony_ci using DashFactoryFn = sk_sp<SkPathEffect>(*)(); 1107cb93a386Sopenharmony_ci for (DashFactoryFn md : {&make_dash, &make_null_dash}) { 1108cb93a386Sopenharmony_ci SkPaint dashFill; 1109cb93a386Sopenharmony_ci dashFill.setPathEffect((*md)()); 1110cb93a386Sopenharmony_ci TestCase dashFillCase(geo, dashFill, reporter); 1111cb93a386Sopenharmony_ci 1112cb93a386Sopenharmony_ci TestCase fillCase(geo, SkPaint(), reporter); 1113cb93a386Sopenharmony_ci dashFillCase.compare(reporter, fillCase, TestCase::kAllSame_ComparisonExpecation); 1114cb93a386Sopenharmony_ci } 1115cb93a386Sopenharmony_ci} 1116cb93a386Sopenharmony_ci 1117cb93a386Sopenharmony_civoid test_null_dash(skiatest::Reporter* reporter, const Geo& geo) { 1118cb93a386Sopenharmony_ci SkPaint fill; 1119cb93a386Sopenharmony_ci SkPaint stroke; 1120cb93a386Sopenharmony_ci stroke.setStyle(SkPaint::kStroke_Style); 1121cb93a386Sopenharmony_ci stroke.setStrokeWidth(1.f); 1122cb93a386Sopenharmony_ci SkPaint dash; 1123cb93a386Sopenharmony_ci dash.setStyle(SkPaint::kStroke_Style); 1124cb93a386Sopenharmony_ci dash.setStrokeWidth(1.f); 1125cb93a386Sopenharmony_ci dash.setPathEffect(make_dash()); 1126cb93a386Sopenharmony_ci SkPaint nullDash; 1127cb93a386Sopenharmony_ci nullDash.setStyle(SkPaint::kStroke_Style); 1128cb93a386Sopenharmony_ci nullDash.setStrokeWidth(1.f); 1129cb93a386Sopenharmony_ci nullDash.setPathEffect(make_null_dash()); 1130cb93a386Sopenharmony_ci 1131cb93a386Sopenharmony_ci TestCase fillCase(geo, fill, reporter); 1132cb93a386Sopenharmony_ci TestCase strokeCase(geo, stroke, reporter); 1133cb93a386Sopenharmony_ci TestCase dashCase(geo, dash, reporter); 1134cb93a386Sopenharmony_ci TestCase nullDashCase(geo, nullDash, reporter); 1135cb93a386Sopenharmony_ci 1136cb93a386Sopenharmony_ci // We expect the null dash to be ignored so nullDashCase should match strokeCase, always. 1137cb93a386Sopenharmony_ci nullDashCase.compare(reporter, strokeCase, TestCase::kAllSame_ComparisonExpecation); 1138cb93a386Sopenharmony_ci // Check whether the fillCase or strokeCase/nullDashCase would undergo a geometric tranformation 1139cb93a386Sopenharmony_ci // on construction in order to determine how to compare the fill and stroke. 1140cb93a386Sopenharmony_ci if (geo.fillChangesGeom() || geo.strokeIsConvertedToFill()) { 1141cb93a386Sopenharmony_ci nullDashCase.compare(reporter, fillCase, TestCase::kAllDifferent_ComparisonExpecation); 1142cb93a386Sopenharmony_ci } else { 1143cb93a386Sopenharmony_ci nullDashCase.compare(reporter, fillCase, TestCase::kSameUpToStroke_ComparisonExpecation); 1144cb93a386Sopenharmony_ci } 1145cb93a386Sopenharmony_ci // In the null dash case we may immediately convert to a fill, but not for the normal dash case. 1146cb93a386Sopenharmony_ci if (geo.strokeIsConvertedToFill()) { 1147cb93a386Sopenharmony_ci nullDashCase.compare(reporter, dashCase, TestCase::kAllDifferent_ComparisonExpecation); 1148cb93a386Sopenharmony_ci } else { 1149cb93a386Sopenharmony_ci nullDashCase.compare(reporter, dashCase, TestCase::kSameUpToPE_ComparisonExpecation); 1150cb93a386Sopenharmony_ci } 1151cb93a386Sopenharmony_ci} 1152cb93a386Sopenharmony_ci 1153cb93a386Sopenharmony_civoid test_path_effect_makes_rrect(skiatest::Reporter* reporter, const Geo& geo) { 1154cb93a386Sopenharmony_ci /** 1155cb93a386Sopenharmony_ci * This path effect takes any input path and turns it into a rrect. It passes through stroke 1156cb93a386Sopenharmony_ci * info. 1157cb93a386Sopenharmony_ci */ 1158cb93a386Sopenharmony_ci class RRectPathEffect : SkPathEffectBase { 1159cb93a386Sopenharmony_ci public: 1160cb93a386Sopenharmony_ci static const SkRRect& RRect() { 1161cb93a386Sopenharmony_ci static const SkRRect kRRect = SkRRect::MakeRectXY(SkRect::MakeWH(12, 12), 3, 5); 1162cb93a386Sopenharmony_ci return kRRect; 1163cb93a386Sopenharmony_ci } 1164cb93a386Sopenharmony_ci 1165cb93a386Sopenharmony_ci static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new RRectPathEffect); } 1166cb93a386Sopenharmony_ci Factory getFactory() const override { return nullptr; } 1167cb93a386Sopenharmony_ci const char* getTypeName() const override { return nullptr; } 1168cb93a386Sopenharmony_ci 1169cb93a386Sopenharmony_ci protected: 1170cb93a386Sopenharmony_ci bool onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, 1171cb93a386Sopenharmony_ci const SkRect* cullR, const SkMatrix&) const override { 1172cb93a386Sopenharmony_ci dst->reset(); 1173cb93a386Sopenharmony_ci dst->addRRect(RRect()); 1174cb93a386Sopenharmony_ci return true; 1175cb93a386Sopenharmony_ci } 1176cb93a386Sopenharmony_ci 1177cb93a386Sopenharmony_ci bool computeFastBounds(SkRect* bounds) const override { 1178cb93a386Sopenharmony_ci if (bounds) { 1179cb93a386Sopenharmony_ci *bounds = RRect().getBounds(); 1180cb93a386Sopenharmony_ci } 1181cb93a386Sopenharmony_ci return true; 1182cb93a386Sopenharmony_ci } 1183cb93a386Sopenharmony_ci 1184cb93a386Sopenharmony_ci private: 1185cb93a386Sopenharmony_ci RRectPathEffect() {} 1186cb93a386Sopenharmony_ci }; 1187cb93a386Sopenharmony_ci 1188cb93a386Sopenharmony_ci SkPaint fill; 1189cb93a386Sopenharmony_ci TestCase fillGeoCase(geo, fill, reporter); 1190cb93a386Sopenharmony_ci 1191cb93a386Sopenharmony_ci SkPaint pe; 1192cb93a386Sopenharmony_ci pe.setPathEffect(RRectPathEffect::Make()); 1193cb93a386Sopenharmony_ci TestCase geoPECase(geo, pe, reporter); 1194cb93a386Sopenharmony_ci 1195cb93a386Sopenharmony_ci SkPaint peStroke; 1196cb93a386Sopenharmony_ci peStroke.setPathEffect(RRectPathEffect::Make()); 1197cb93a386Sopenharmony_ci peStroke.setStrokeWidth(2.f); 1198cb93a386Sopenharmony_ci peStroke.setStyle(SkPaint::kStroke_Style); 1199cb93a386Sopenharmony_ci TestCase geoPEStrokeCase(geo, peStroke, reporter); 1200cb93a386Sopenharmony_ci 1201cb93a386Sopenharmony_ci // Check whether constructing the filled case would cause the base shape to have a different 1202cb93a386Sopenharmony_ci // geometry (because of a geometric transformation upon initial GrStyledShape construction). 1203cb93a386Sopenharmony_ci if (geo.fillChangesGeom()) { 1204cb93a386Sopenharmony_ci fillGeoCase.compare(reporter, geoPECase, TestCase::kAllDifferent_ComparisonExpecation); 1205cb93a386Sopenharmony_ci fillGeoCase.compare(reporter, geoPEStrokeCase, 1206cb93a386Sopenharmony_ci TestCase::kAllDifferent_ComparisonExpecation); 1207cb93a386Sopenharmony_ci } else { 1208cb93a386Sopenharmony_ci fillGeoCase.compare(reporter, geoPECase, TestCase::kSameUpToPE_ComparisonExpecation); 1209cb93a386Sopenharmony_ci fillGeoCase.compare(reporter, geoPEStrokeCase, TestCase::kSameUpToPE_ComparisonExpecation); 1210cb93a386Sopenharmony_ci } 1211cb93a386Sopenharmony_ci geoPECase.compare(reporter, geoPEStrokeCase, 1212cb93a386Sopenharmony_ci TestCase::kSameUpToStroke_ComparisonExpecation); 1213cb93a386Sopenharmony_ci 1214cb93a386Sopenharmony_ci TestCase rrectFillCase(reporter, RRectPathEffect::RRect(), fill); 1215cb93a386Sopenharmony_ci SkPaint stroke = peStroke; 1216cb93a386Sopenharmony_ci stroke.setPathEffect(nullptr); 1217cb93a386Sopenharmony_ci TestCase rrectStrokeCase(reporter, RRectPathEffect::RRect(), stroke); 1218cb93a386Sopenharmony_ci 1219cb93a386Sopenharmony_ci SkRRect rrect; 1220cb93a386Sopenharmony_ci // Applying the path effect should make a SkRRect shape. There is no further stroking in the 1221cb93a386Sopenharmony_ci // geoPECase, so the full style should be the same as just the PE. 1222cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, geoPECase.appliedPathEffectShape().asRRect(&rrect, nullptr, nullptr, 1223cb93a386Sopenharmony_ci nullptr)); 1224cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, rrect == RRectPathEffect::RRect()); 1225cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, geoPECase.appliedPathEffectKey() == rrectFillCase.baseKey()); 1226cb93a386Sopenharmony_ci 1227cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, geoPECase.appliedFullStyleShape().asRRect(&rrect, nullptr, nullptr, 1228cb93a386Sopenharmony_ci nullptr)); 1229cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, rrect == RRectPathEffect::RRect()); 1230cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, geoPECase.appliedFullStyleKey() == rrectFillCase.baseKey()); 1231cb93a386Sopenharmony_ci 1232cb93a386Sopenharmony_ci // In the PE+stroke case applying the full style should be the same as just stroking the rrect. 1233cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectShape().asRRect(&rrect, nullptr, 1234cb93a386Sopenharmony_ci nullptr, nullptr)); 1235cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, rrect == RRectPathEffect::RRect()); 1236cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectKey() == rrectFillCase.baseKey()); 1237cb93a386Sopenharmony_ci 1238cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, !geoPEStrokeCase.appliedFullStyleShape().asRRect(&rrect, nullptr, 1239cb93a386Sopenharmony_ci nullptr, nullptr)); 1240cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedFullStyleKey() == 1241cb93a386Sopenharmony_ci rrectStrokeCase.appliedFullStyleKey()); 1242cb93a386Sopenharmony_ci} 1243cb93a386Sopenharmony_ci 1244cb93a386Sopenharmony_civoid test_unknown_path_effect(skiatest::Reporter* reporter, const Geo& geo) { 1245cb93a386Sopenharmony_ci /** 1246cb93a386Sopenharmony_ci * This path effect just adds two lineTos to the input path. 1247cb93a386Sopenharmony_ci */ 1248cb93a386Sopenharmony_ci class AddLineTosPathEffect : SkPathEffectBase { 1249cb93a386Sopenharmony_ci public: 1250cb93a386Sopenharmony_ci static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new AddLineTosPathEffect); } 1251cb93a386Sopenharmony_ci Factory getFactory() const override { return nullptr; } 1252cb93a386Sopenharmony_ci const char* getTypeName() const override { return nullptr; } 1253cb93a386Sopenharmony_ci 1254cb93a386Sopenharmony_ci protected: 1255cb93a386Sopenharmony_ci bool onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, 1256cb93a386Sopenharmony_ci const SkRect* cullR, const SkMatrix&) const override { 1257cb93a386Sopenharmony_ci *dst = src; 1258cb93a386Sopenharmony_ci // To avoid triggering data-based keying of paths with few verbs we add many segments. 1259cb93a386Sopenharmony_ci for (int i = 0; i < 100; ++i) { 1260cb93a386Sopenharmony_ci dst->lineTo(SkIntToScalar(i), SkIntToScalar(i)); 1261cb93a386Sopenharmony_ci } 1262cb93a386Sopenharmony_ci return true; 1263cb93a386Sopenharmony_ci } 1264cb93a386Sopenharmony_ci bool computeFastBounds(SkRect* bounds) const override { 1265cb93a386Sopenharmony_ci if (bounds) { 1266cb93a386Sopenharmony_ci SkRectPriv::GrowToInclude(bounds, {0, 0}); 1267cb93a386Sopenharmony_ci SkRectPriv::GrowToInclude(bounds, {100, 100}); 1268cb93a386Sopenharmony_ci } 1269cb93a386Sopenharmony_ci return true; 1270cb93a386Sopenharmony_ci } 1271cb93a386Sopenharmony_ci private: 1272cb93a386Sopenharmony_ci AddLineTosPathEffect() {} 1273cb93a386Sopenharmony_ci }; 1274cb93a386Sopenharmony_ci 1275cb93a386Sopenharmony_ci // This path effect should make the keys invalid when it is applied. We only produce a path 1276cb93a386Sopenharmony_ci // effect key for dash path effects. So the only way another arbitrary path effect can produce 1277cb93a386Sopenharmony_ci // a styled result with a key is to produce a non-path shape that has a purely geometric key. 1278cb93a386Sopenharmony_ci SkPaint peStroke; 1279cb93a386Sopenharmony_ci peStroke.setPathEffect(AddLineTosPathEffect::Make()); 1280cb93a386Sopenharmony_ci peStroke.setStrokeWidth(2.f); 1281cb93a386Sopenharmony_ci peStroke.setStyle(SkPaint::kStroke_Style); 1282cb93a386Sopenharmony_ci TestCase geoPEStrokeCase(geo, peStroke, reporter); 1283cb93a386Sopenharmony_ci TestCase::SelfExpectations expectations; 1284cb93a386Sopenharmony_ci expectations.fPEHasEffect = true; 1285cb93a386Sopenharmony_ci expectations.fPEHasValidKey = false; 1286cb93a386Sopenharmony_ci expectations.fStrokeApplies = true; 1287cb93a386Sopenharmony_ci geoPEStrokeCase.testExpectations(reporter, expectations); 1288cb93a386Sopenharmony_ci} 1289cb93a386Sopenharmony_ci 1290cb93a386Sopenharmony_civoid test_make_hairline_path_effect(skiatest::Reporter* reporter, const Geo& geo) { 1291cb93a386Sopenharmony_ci /** 1292cb93a386Sopenharmony_ci * This path effect just changes the stroke rec to hairline. 1293cb93a386Sopenharmony_ci */ 1294cb93a386Sopenharmony_ci class MakeHairlinePathEffect : SkPathEffectBase { 1295cb93a386Sopenharmony_ci public: 1296cb93a386Sopenharmony_ci static sk_sp<SkPathEffect> Make() { 1297cb93a386Sopenharmony_ci return sk_sp<SkPathEffect>(new MakeHairlinePathEffect); 1298cb93a386Sopenharmony_ci } 1299cb93a386Sopenharmony_ci Factory getFactory() const override { return nullptr; } 1300cb93a386Sopenharmony_ci const char* getTypeName() const override { return nullptr; } 1301cb93a386Sopenharmony_ci 1302cb93a386Sopenharmony_ci protected: 1303cb93a386Sopenharmony_ci bool onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec* strokeRec, 1304cb93a386Sopenharmony_ci const SkRect* cullR, const SkMatrix&) const override { 1305cb93a386Sopenharmony_ci *dst = src; 1306cb93a386Sopenharmony_ci strokeRec->setHairlineStyle(); 1307cb93a386Sopenharmony_ci return true; 1308cb93a386Sopenharmony_ci } 1309cb93a386Sopenharmony_ci private: 1310cb93a386Sopenharmony_ci bool computeFastBounds(SkRect* bounds) const override { return true; } 1311cb93a386Sopenharmony_ci 1312cb93a386Sopenharmony_ci MakeHairlinePathEffect() {} 1313cb93a386Sopenharmony_ci }; 1314cb93a386Sopenharmony_ci 1315cb93a386Sopenharmony_ci SkPaint fill; 1316cb93a386Sopenharmony_ci SkPaint pe; 1317cb93a386Sopenharmony_ci pe.setPathEffect(MakeHairlinePathEffect::Make()); 1318cb93a386Sopenharmony_ci 1319cb93a386Sopenharmony_ci TestCase peCase(geo, pe, reporter); 1320cb93a386Sopenharmony_ci 1321cb93a386Sopenharmony_ci SkPath a, b, c; 1322cb93a386Sopenharmony_ci peCase.baseShape().asPath(&a); 1323cb93a386Sopenharmony_ci peCase.appliedPathEffectShape().asPath(&b); 1324cb93a386Sopenharmony_ci peCase.appliedFullStyleShape().asPath(&c); 1325cb93a386Sopenharmony_ci if (geo.isNonPath(pe)) { 1326cb93a386Sopenharmony_ci // RRect types can have a change in start index or direction after the PE is applied. This 1327cb93a386Sopenharmony_ci // is because once the PE is applied, GrStyledShape may canonicalize the dir and index since 1328cb93a386Sopenharmony_ci // it is not germane to the styling any longer. 1329cb93a386Sopenharmony_ci // Instead we just check that the paths would fill the same both before and after styling. 1330cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, paths_fill_same(a, b)); 1331cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, paths_fill_same(a, c)); 1332cb93a386Sopenharmony_ci } else { 1333cb93a386Sopenharmony_ci // The base shape cannot perform canonicalization on the path's fill type because of an 1334cb93a386Sopenharmony_ci // unknown path effect. However, after the path effect is applied the resulting hairline 1335cb93a386Sopenharmony_ci // shape will canonicalize the path fill type since hairlines (and stroking in general) 1336cb93a386Sopenharmony_ci // don't distinguish between even/odd and non-zero winding. 1337cb93a386Sopenharmony_ci a.setFillType(b.getFillType()); 1338cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, a == b); 1339cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, a == c); 1340cb93a386Sopenharmony_ci // If the resulting path is small enough then it will have a key. 1341cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, paths_fill_same(a, b)); 1342cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, paths_fill_same(a, c)); 1343cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, peCase.appliedPathEffectKey().empty()); 1344cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, peCase.appliedFullStyleKey().empty()); 1345cb93a386Sopenharmony_ci } 1346cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, peCase.appliedPathEffectShape().style().isSimpleHairline()); 1347cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, peCase.appliedFullStyleShape().style().isSimpleHairline()); 1348cb93a386Sopenharmony_ci} 1349cb93a386Sopenharmony_ci 1350cb93a386Sopenharmony_civoid test_volatile_path(skiatest::Reporter* reporter, const Geo& geo) { 1351cb93a386Sopenharmony_ci SkPath vPath = geo.path(); 1352cb93a386Sopenharmony_ci vPath.setIsVolatile(true); 1353cb93a386Sopenharmony_ci 1354cb93a386Sopenharmony_ci SkPaint dashAndStroke; 1355cb93a386Sopenharmony_ci dashAndStroke.setPathEffect(make_dash()); 1356cb93a386Sopenharmony_ci dashAndStroke.setStrokeWidth(2.f); 1357cb93a386Sopenharmony_ci dashAndStroke.setStyle(SkPaint::kStroke_Style); 1358cb93a386Sopenharmony_ci TestCase volatileCase(reporter, vPath, dashAndStroke); 1359cb93a386Sopenharmony_ci // We expect a shape made from a volatile path to have a key iff the shape is recognized 1360cb93a386Sopenharmony_ci // as a specialized geometry. 1361cb93a386Sopenharmony_ci if (geo.isNonPath(dashAndStroke)) { 1362cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, SkToBool(volatileCase.baseKey().count())); 1363cb93a386Sopenharmony_ci // In this case all the keys should be identical to the non-volatile case. 1364cb93a386Sopenharmony_ci TestCase nonVolatileCase(reporter, geo.path(), dashAndStroke); 1365cb93a386Sopenharmony_ci volatileCase.compare(reporter, nonVolatileCase, TestCase::kAllSame_ComparisonExpecation); 1366cb93a386Sopenharmony_ci } else { 1367cb93a386Sopenharmony_ci // None of the keys should be valid. 1368cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, !SkToBool(volatileCase.baseKey().count())); 1369cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, !SkToBool(volatileCase.appliedPathEffectKey().count())); 1370cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, !SkToBool(volatileCase.appliedFullStyleKey().count())); 1371cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, !SkToBool(volatileCase.appliedPathEffectThenStrokeKey().count())); 1372cb93a386Sopenharmony_ci } 1373cb93a386Sopenharmony_ci} 1374cb93a386Sopenharmony_ci 1375cb93a386Sopenharmony_civoid test_path_effect_makes_empty_shape(skiatest::Reporter* reporter, const Geo& geo) { 1376cb93a386Sopenharmony_ci /** 1377cb93a386Sopenharmony_ci * This path effect returns an empty path (possibly inverted) 1378cb93a386Sopenharmony_ci */ 1379cb93a386Sopenharmony_ci class EmptyPathEffect : SkPathEffectBase { 1380cb93a386Sopenharmony_ci public: 1381cb93a386Sopenharmony_ci static sk_sp<SkPathEffect> Make(bool invert) { 1382cb93a386Sopenharmony_ci return sk_sp<SkPathEffect>(new EmptyPathEffect(invert)); 1383cb93a386Sopenharmony_ci } 1384cb93a386Sopenharmony_ci Factory getFactory() const override { return nullptr; } 1385cb93a386Sopenharmony_ci const char* getTypeName() const override { return nullptr; } 1386cb93a386Sopenharmony_ci protected: 1387cb93a386Sopenharmony_ci bool onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, 1388cb93a386Sopenharmony_ci const SkRect* cullR, const SkMatrix&) const override { 1389cb93a386Sopenharmony_ci dst->reset(); 1390cb93a386Sopenharmony_ci if (fInvert) { 1391cb93a386Sopenharmony_ci dst->toggleInverseFillType(); 1392cb93a386Sopenharmony_ci } 1393cb93a386Sopenharmony_ci return true; 1394cb93a386Sopenharmony_ci } 1395cb93a386Sopenharmony_ci bool computeFastBounds(SkRect* bounds) const override { 1396cb93a386Sopenharmony_ci if (bounds) { 1397cb93a386Sopenharmony_ci *bounds = { 0, 0, 0, 0 }; 1398cb93a386Sopenharmony_ci } 1399cb93a386Sopenharmony_ci return true; 1400cb93a386Sopenharmony_ci } 1401cb93a386Sopenharmony_ci private: 1402cb93a386Sopenharmony_ci bool fInvert; 1403cb93a386Sopenharmony_ci EmptyPathEffect(bool invert) : fInvert(invert) {} 1404cb93a386Sopenharmony_ci }; 1405cb93a386Sopenharmony_ci 1406cb93a386Sopenharmony_ci SkPath emptyPath; 1407cb93a386Sopenharmony_ci GrStyledShape emptyShape(emptyPath); 1408cb93a386Sopenharmony_ci Key emptyKey; 1409cb93a386Sopenharmony_ci make_key(&emptyKey, emptyShape); 1410cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, emptyShape.isEmpty()); 1411cb93a386Sopenharmony_ci 1412cb93a386Sopenharmony_ci emptyPath.toggleInverseFillType(); 1413cb93a386Sopenharmony_ci GrStyledShape invertedEmptyShape(emptyPath); 1414cb93a386Sopenharmony_ci Key invertedEmptyKey; 1415cb93a386Sopenharmony_ci make_key(&invertedEmptyKey, invertedEmptyShape); 1416cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, invertedEmptyShape.isEmpty()); 1417cb93a386Sopenharmony_ci 1418cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, invertedEmptyKey != emptyKey); 1419cb93a386Sopenharmony_ci 1420cb93a386Sopenharmony_ci SkPaint pe; 1421cb93a386Sopenharmony_ci pe.setPathEffect(EmptyPathEffect::Make(false)); 1422cb93a386Sopenharmony_ci TestCase geoPECase(geo, pe, reporter); 1423cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, geoPECase.appliedFullStyleKey() == emptyKey); 1424cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, geoPECase.appliedPathEffectKey() == emptyKey); 1425cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, geoPECase.appliedPathEffectThenStrokeKey() == emptyKey); 1426cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, geoPECase.appliedPathEffectShape().isEmpty()); 1427cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, geoPECase.appliedFullStyleShape().isEmpty()); 1428cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, !geoPECase.appliedPathEffectShape().inverseFilled()); 1429cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, !geoPECase.appliedFullStyleShape().inverseFilled()); 1430cb93a386Sopenharmony_ci 1431cb93a386Sopenharmony_ci SkPaint peStroke; 1432cb93a386Sopenharmony_ci peStroke.setPathEffect(EmptyPathEffect::Make(false)); 1433cb93a386Sopenharmony_ci peStroke.setStrokeWidth(2.f); 1434cb93a386Sopenharmony_ci peStroke.setStyle(SkPaint::kStroke_Style); 1435cb93a386Sopenharmony_ci TestCase geoPEStrokeCase(geo, peStroke, reporter); 1436cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedFullStyleKey() == emptyKey); 1437cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectKey() == emptyKey); 1438cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectThenStrokeKey() == emptyKey); 1439cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectShape().isEmpty()); 1440cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedFullStyleShape().isEmpty()); 1441cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, !geoPEStrokeCase.appliedPathEffectShape().inverseFilled()); 1442cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, !geoPEStrokeCase.appliedFullStyleShape().inverseFilled()); 1443cb93a386Sopenharmony_ci pe.setPathEffect(EmptyPathEffect::Make(true)); 1444cb93a386Sopenharmony_ci 1445cb93a386Sopenharmony_ci TestCase geoPEInvertCase(geo, pe, reporter); 1446cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, geoPEInvertCase.appliedFullStyleKey() == invertedEmptyKey); 1447cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, geoPEInvertCase.appliedPathEffectKey() == invertedEmptyKey); 1448cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, geoPEInvertCase.appliedPathEffectThenStrokeKey() == invertedEmptyKey); 1449cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, geoPEInvertCase.appliedPathEffectShape().isEmpty()); 1450cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, geoPEInvertCase.appliedFullStyleShape().isEmpty()); 1451cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, geoPEInvertCase.appliedPathEffectShape().inverseFilled()); 1452cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, geoPEInvertCase.appliedFullStyleShape().inverseFilled()); 1453cb93a386Sopenharmony_ci 1454cb93a386Sopenharmony_ci peStroke.setPathEffect(EmptyPathEffect::Make(true)); 1455cb93a386Sopenharmony_ci TestCase geoPEInvertStrokeCase(geo, peStroke, reporter); 1456cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, geoPEInvertStrokeCase.appliedFullStyleKey() == invertedEmptyKey); 1457cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, geoPEInvertStrokeCase.appliedPathEffectKey() == invertedEmptyKey); 1458cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, 1459cb93a386Sopenharmony_ci geoPEInvertStrokeCase.appliedPathEffectThenStrokeKey() == invertedEmptyKey); 1460cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, geoPEInvertStrokeCase.appliedPathEffectShape().isEmpty()); 1461cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, geoPEInvertStrokeCase.appliedFullStyleShape().isEmpty()); 1462cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, geoPEInvertStrokeCase.appliedPathEffectShape().inverseFilled()); 1463cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, geoPEInvertStrokeCase.appliedFullStyleShape().inverseFilled()); 1464cb93a386Sopenharmony_ci} 1465cb93a386Sopenharmony_ci 1466cb93a386Sopenharmony_civoid test_path_effect_fails(skiatest::Reporter* reporter, const Geo& geo) { 1467cb93a386Sopenharmony_ci /** 1468cb93a386Sopenharmony_ci * This path effect always fails to apply. 1469cb93a386Sopenharmony_ci */ 1470cb93a386Sopenharmony_ci class FailurePathEffect : SkPathEffectBase { 1471cb93a386Sopenharmony_ci public: 1472cb93a386Sopenharmony_ci static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new FailurePathEffect); } 1473cb93a386Sopenharmony_ci Factory getFactory() const override { return nullptr; } 1474cb93a386Sopenharmony_ci const char* getTypeName() const override { return nullptr; } 1475cb93a386Sopenharmony_ci protected: 1476cb93a386Sopenharmony_ci bool onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, 1477cb93a386Sopenharmony_ci const SkRect* cullR, const SkMatrix&) const override { 1478cb93a386Sopenharmony_ci return false; 1479cb93a386Sopenharmony_ci } 1480cb93a386Sopenharmony_ci private: 1481cb93a386Sopenharmony_ci bool computeFastBounds(SkRect* bounds) const override { return false; } 1482cb93a386Sopenharmony_ci 1483cb93a386Sopenharmony_ci FailurePathEffect() {} 1484cb93a386Sopenharmony_ci }; 1485cb93a386Sopenharmony_ci 1486cb93a386Sopenharmony_ci SkPaint fill; 1487cb93a386Sopenharmony_ci TestCase fillCase(geo, fill, reporter); 1488cb93a386Sopenharmony_ci 1489cb93a386Sopenharmony_ci SkPaint pe; 1490cb93a386Sopenharmony_ci pe.setPathEffect(FailurePathEffect::Make()); 1491cb93a386Sopenharmony_ci TestCase peCase(geo, pe, reporter); 1492cb93a386Sopenharmony_ci 1493cb93a386Sopenharmony_ci SkPaint stroke; 1494cb93a386Sopenharmony_ci stroke.setStrokeWidth(2.f); 1495cb93a386Sopenharmony_ci stroke.setStyle(SkPaint::kStroke_Style); 1496cb93a386Sopenharmony_ci TestCase strokeCase(geo, stroke, reporter); 1497cb93a386Sopenharmony_ci 1498cb93a386Sopenharmony_ci SkPaint peStroke = stroke; 1499cb93a386Sopenharmony_ci peStroke.setPathEffect(FailurePathEffect::Make()); 1500cb93a386Sopenharmony_ci TestCase peStrokeCase(geo, peStroke, reporter); 1501cb93a386Sopenharmony_ci 1502cb93a386Sopenharmony_ci // In general the path effect failure can cause some of the TestCase::compare() tests to fail 1503cb93a386Sopenharmony_ci // for at least two reasons: 1) We will initially treat the shape as unkeyable because of the 1504cb93a386Sopenharmony_ci // path effect, but then when the path effect fails we can key it. 2) GrStyledShape will change 1505cb93a386Sopenharmony_ci // its mind about whether a unclosed rect is actually rect. The path effect initially bars us 1506cb93a386Sopenharmony_ci // from closing it but after the effect fails we can (for the fill+pe case). This causes 1507cb93a386Sopenharmony_ci // different routes through GrStyledShape to have equivalent but different representations of 1508cb93a386Sopenharmony_ci // the path (closed or not) but that fill the same. 1509cb93a386Sopenharmony_ci SkPath a; 1510cb93a386Sopenharmony_ci SkPath b; 1511cb93a386Sopenharmony_ci fillCase.appliedPathEffectShape().asPath(&a); 1512cb93a386Sopenharmony_ci peCase.appliedPathEffectShape().asPath(&b); 1513cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, paths_fill_same(a, b)); 1514cb93a386Sopenharmony_ci 1515cb93a386Sopenharmony_ci fillCase.appliedFullStyleShape().asPath(&a); 1516cb93a386Sopenharmony_ci peCase.appliedFullStyleShape().asPath(&b); 1517cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, paths_fill_same(a, b)); 1518cb93a386Sopenharmony_ci 1519cb93a386Sopenharmony_ci strokeCase.appliedPathEffectShape().asPath(&a); 1520cb93a386Sopenharmony_ci peStrokeCase.appliedPathEffectShape().asPath(&b); 1521cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, paths_fill_same(a, b)); 1522cb93a386Sopenharmony_ci 1523cb93a386Sopenharmony_ci strokeCase.appliedFullStyleShape().asPath(&a); 1524cb93a386Sopenharmony_ci peStrokeCase.appliedFullStyleShape().asPath(&b); 1525cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, paths_fill_same(a, b)); 1526cb93a386Sopenharmony_ci} 1527cb93a386Sopenharmony_ci 1528cb93a386Sopenharmony_ciDEF_TEST(GrStyledShape_empty_shape, reporter) { 1529cb93a386Sopenharmony_ci SkPath emptyPath; 1530cb93a386Sopenharmony_ci SkPath invertedEmptyPath; 1531cb93a386Sopenharmony_ci invertedEmptyPath.toggleInverseFillType(); 1532cb93a386Sopenharmony_ci SkPaint fill; 1533cb93a386Sopenharmony_ci TestCase fillEmptyCase(reporter, emptyPath, fill); 1534cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, fillEmptyCase.baseShape().isEmpty()); 1535cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, fillEmptyCase.appliedPathEffectShape().isEmpty()); 1536cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, fillEmptyCase.appliedFullStyleShape().isEmpty()); 1537cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, !fillEmptyCase.baseShape().inverseFilled()); 1538cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, !fillEmptyCase.appliedPathEffectShape().inverseFilled()); 1539cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, !fillEmptyCase.appliedFullStyleShape().inverseFilled()); 1540cb93a386Sopenharmony_ci TestCase fillInvertedEmptyCase(reporter, invertedEmptyPath, fill); 1541cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, fillInvertedEmptyCase.baseShape().isEmpty()); 1542cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, fillInvertedEmptyCase.appliedPathEffectShape().isEmpty()); 1543cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, fillInvertedEmptyCase.appliedFullStyleShape().isEmpty()); 1544cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, fillInvertedEmptyCase.baseShape().inverseFilled()); 1545cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, fillInvertedEmptyCase.appliedPathEffectShape().inverseFilled()); 1546cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, fillInvertedEmptyCase.appliedFullStyleShape().inverseFilled()); 1547cb93a386Sopenharmony_ci 1548cb93a386Sopenharmony_ci const Key& emptyKey = fillEmptyCase.baseKey(); 1549cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, emptyKey.count()); 1550cb93a386Sopenharmony_ci const Key& inverseEmptyKey = fillInvertedEmptyCase.baseKey(); 1551cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, inverseEmptyKey.count()); 1552cb93a386Sopenharmony_ci TestCase::SelfExpectations expectations; 1553cb93a386Sopenharmony_ci expectations.fStrokeApplies = false; 1554cb93a386Sopenharmony_ci expectations.fPEHasEffect = false; 1555cb93a386Sopenharmony_ci // This will test whether applying style preserves emptiness 1556cb93a386Sopenharmony_ci fillEmptyCase.testExpectations(reporter, expectations); 1557cb93a386Sopenharmony_ci fillInvertedEmptyCase.testExpectations(reporter, expectations); 1558cb93a386Sopenharmony_ci 1559cb93a386Sopenharmony_ci // Stroking an empty path should have no effect 1560cb93a386Sopenharmony_ci SkPaint stroke; 1561cb93a386Sopenharmony_ci stroke.setStrokeWidth(2.f); 1562cb93a386Sopenharmony_ci stroke.setStyle(SkPaint::kStroke_Style); 1563cb93a386Sopenharmony_ci stroke.setStrokeJoin(SkPaint::kRound_Join); 1564cb93a386Sopenharmony_ci stroke.setStrokeCap(SkPaint::kRound_Cap); 1565cb93a386Sopenharmony_ci TestCase strokeEmptyCase(reporter, emptyPath, stroke); 1566cb93a386Sopenharmony_ci strokeEmptyCase.compare(reporter, fillEmptyCase, TestCase::kAllSame_ComparisonExpecation); 1567cb93a386Sopenharmony_ci TestCase strokeInvertedEmptyCase(reporter, invertedEmptyPath, stroke); 1568cb93a386Sopenharmony_ci strokeInvertedEmptyCase.compare(reporter, fillInvertedEmptyCase, 1569cb93a386Sopenharmony_ci TestCase::kAllSame_ComparisonExpecation); 1570cb93a386Sopenharmony_ci 1571cb93a386Sopenharmony_ci // Dashing and stroking an empty path should have no effect 1572cb93a386Sopenharmony_ci SkPaint dashAndStroke; 1573cb93a386Sopenharmony_ci dashAndStroke.setPathEffect(make_dash()); 1574cb93a386Sopenharmony_ci dashAndStroke.setStrokeWidth(2.f); 1575cb93a386Sopenharmony_ci dashAndStroke.setStyle(SkPaint::kStroke_Style); 1576cb93a386Sopenharmony_ci TestCase dashAndStrokeEmptyCase(reporter, emptyPath, dashAndStroke); 1577cb93a386Sopenharmony_ci dashAndStrokeEmptyCase.compare(reporter, fillEmptyCase, 1578cb93a386Sopenharmony_ci TestCase::kAllSame_ComparisonExpecation); 1579cb93a386Sopenharmony_ci TestCase dashAndStrokeInvertexEmptyCase(reporter, invertedEmptyPath, dashAndStroke); 1580cb93a386Sopenharmony_ci // Dashing ignores inverseness so this is equivalent to the non-inverted empty fill. 1581cb93a386Sopenharmony_ci dashAndStrokeInvertexEmptyCase.compare(reporter, fillEmptyCase, 1582cb93a386Sopenharmony_ci TestCase::kAllSame_ComparisonExpecation); 1583cb93a386Sopenharmony_ci 1584cb93a386Sopenharmony_ci // A shape made from an empty rrect should behave the same as an empty path when filled and 1585cb93a386Sopenharmony_ci // when stroked. The shape is closed so it does not produce caps when stroked. When dashed there 1586cb93a386Sopenharmony_ci // is no path to dash along, making it equivalent as well. 1587cb93a386Sopenharmony_ci SkRRect emptyRRect = SkRRect::MakeEmpty(); 1588cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, emptyRRect.getType() == SkRRect::kEmpty_Type); 1589cb93a386Sopenharmony_ci 1590cb93a386Sopenharmony_ci TestCase fillEmptyRRectCase(reporter, emptyRRect, fill); 1591cb93a386Sopenharmony_ci fillEmptyRRectCase.compare(reporter, fillEmptyCase, TestCase::kAllSame_ComparisonExpecation); 1592cb93a386Sopenharmony_ci 1593cb93a386Sopenharmony_ci TestCase strokeEmptyRRectCase(reporter, emptyRRect, stroke); 1594cb93a386Sopenharmony_ci strokeEmptyRRectCase.compare(reporter, strokeEmptyCase, 1595cb93a386Sopenharmony_ci TestCase::kAllSame_ComparisonExpecation); 1596cb93a386Sopenharmony_ci 1597cb93a386Sopenharmony_ci TestCase dashAndStrokeEmptyRRectCase(reporter, emptyRRect, dashAndStroke); 1598cb93a386Sopenharmony_ci dashAndStrokeEmptyRRectCase.compare(reporter, fillEmptyCase, 1599cb93a386Sopenharmony_ci TestCase::kAllSame_ComparisonExpecation); 1600cb93a386Sopenharmony_ci 1601cb93a386Sopenharmony_ci static constexpr SkPathDirection kDir = SkPathDirection::kCCW; 1602cb93a386Sopenharmony_ci static constexpr int kStart = 0; 1603cb93a386Sopenharmony_ci 1604cb93a386Sopenharmony_ci TestCase fillInvertedEmptyRRectCase(reporter, emptyRRect, kDir, kStart, true, GrStyle(fill)); 1605cb93a386Sopenharmony_ci fillInvertedEmptyRRectCase.compare(reporter, fillInvertedEmptyCase, 1606cb93a386Sopenharmony_ci TestCase::kAllSame_ComparisonExpecation); 1607cb93a386Sopenharmony_ci 1608cb93a386Sopenharmony_ci TestCase strokeInvertedEmptyRRectCase(reporter, emptyRRect, kDir, kStart, true, 1609cb93a386Sopenharmony_ci GrStyle(stroke)); 1610cb93a386Sopenharmony_ci strokeInvertedEmptyRRectCase.compare(reporter, strokeInvertedEmptyCase, 1611cb93a386Sopenharmony_ci TestCase::kAllSame_ComparisonExpecation); 1612cb93a386Sopenharmony_ci 1613cb93a386Sopenharmony_ci TestCase dashAndStrokeEmptyInvertedRRectCase(reporter, emptyRRect, kDir, kStart, true, 1614cb93a386Sopenharmony_ci GrStyle(dashAndStroke)); 1615cb93a386Sopenharmony_ci dashAndStrokeEmptyInvertedRRectCase.compare(reporter, fillEmptyCase, 1616cb93a386Sopenharmony_ci TestCase::kAllSame_ComparisonExpecation); 1617cb93a386Sopenharmony_ci 1618cb93a386Sopenharmony_ci // Same for a rect. 1619cb93a386Sopenharmony_ci SkRect emptyRect = SkRect::MakeEmpty(); 1620cb93a386Sopenharmony_ci TestCase fillEmptyRectCase(reporter, emptyRect, fill); 1621cb93a386Sopenharmony_ci fillEmptyRectCase.compare(reporter, fillEmptyCase, TestCase::kAllSame_ComparisonExpecation); 1622cb93a386Sopenharmony_ci 1623cb93a386Sopenharmony_ci TestCase dashAndStrokeEmptyRectCase(reporter, emptyRect, dashAndStroke); 1624cb93a386Sopenharmony_ci dashAndStrokeEmptyRectCase.compare(reporter, fillEmptyCase, 1625cb93a386Sopenharmony_ci TestCase::kAllSame_ComparisonExpecation); 1626cb93a386Sopenharmony_ci 1627cb93a386Sopenharmony_ci TestCase dashAndStrokeEmptyInvertedRectCase(reporter, SkRRect::MakeRect(emptyRect), kDir, 1628cb93a386Sopenharmony_ci kStart, true, GrStyle(dashAndStroke)); 1629cb93a386Sopenharmony_ci // Dashing ignores inverseness so this is equivalent to the non-inverted empty fill. 1630cb93a386Sopenharmony_ci dashAndStrokeEmptyInvertedRectCase.compare(reporter, fillEmptyCase, 1631cb93a386Sopenharmony_ci TestCase::kAllSame_ComparisonExpecation); 1632cb93a386Sopenharmony_ci} 1633cb93a386Sopenharmony_ci 1634cb93a386Sopenharmony_ci// rect and oval types have rrect start indices that collapse to the same point. Here we select the 1635cb93a386Sopenharmony_ci// canonical point in these cases. 1636cb93a386Sopenharmony_ciunsigned canonicalize_rrect_start(int s, const SkRRect& rrect) { 1637cb93a386Sopenharmony_ci switch (rrect.getType()) { 1638cb93a386Sopenharmony_ci case SkRRect::kRect_Type: 1639cb93a386Sopenharmony_ci return (s + 1) & 0b110; 1640cb93a386Sopenharmony_ci case SkRRect::kOval_Type: 1641cb93a386Sopenharmony_ci return s & 0b110; 1642cb93a386Sopenharmony_ci default: 1643cb93a386Sopenharmony_ci return s; 1644cb93a386Sopenharmony_ci } 1645cb93a386Sopenharmony_ci} 1646cb93a386Sopenharmony_ci 1647cb93a386Sopenharmony_civoid test_rrect(skiatest::Reporter* r, const SkRRect& rrect) { 1648cb93a386Sopenharmony_ci enum Style { 1649cb93a386Sopenharmony_ci kFill, 1650cb93a386Sopenharmony_ci kStroke, 1651cb93a386Sopenharmony_ci kHairline, 1652cb93a386Sopenharmony_ci kStrokeAndFill 1653cb93a386Sopenharmony_ci }; 1654cb93a386Sopenharmony_ci 1655cb93a386Sopenharmony_ci // SkStrokeRec has no default cons., so init with kFill before calling the setters below. 1656cb93a386Sopenharmony_ci SkStrokeRec strokeRecs[4] { SkStrokeRec::kFill_InitStyle, SkStrokeRec::kFill_InitStyle, 1657cb93a386Sopenharmony_ci SkStrokeRec::kFill_InitStyle, SkStrokeRec::kFill_InitStyle}; 1658cb93a386Sopenharmony_ci strokeRecs[kFill].setFillStyle(); 1659cb93a386Sopenharmony_ci strokeRecs[kStroke].setStrokeStyle(2.f); 1660cb93a386Sopenharmony_ci strokeRecs[kHairline].setHairlineStyle(); 1661cb93a386Sopenharmony_ci strokeRecs[kStrokeAndFill].setStrokeStyle(3.f, true); 1662cb93a386Sopenharmony_ci // Use a bevel join to avoid complications of stroke+filled rects becoming filled rects before 1663cb93a386Sopenharmony_ci // applyStyle() is called. 1664cb93a386Sopenharmony_ci strokeRecs[kStrokeAndFill].setStrokeParams(SkPaint::kButt_Cap, SkPaint::kBevel_Join, 1.f); 1665cb93a386Sopenharmony_ci sk_sp<SkPathEffect> dashEffect = make_dash(); 1666cb93a386Sopenharmony_ci 1667cb93a386Sopenharmony_ci static constexpr Style kStyleCnt = static_cast<Style>(SK_ARRAY_COUNT(strokeRecs)); 1668cb93a386Sopenharmony_ci 1669cb93a386Sopenharmony_ci auto index = [](bool inverted, 1670cb93a386Sopenharmony_ci SkPathDirection dir, 1671cb93a386Sopenharmony_ci unsigned start, 1672cb93a386Sopenharmony_ci Style style, 1673cb93a386Sopenharmony_ci bool dash) -> int { 1674cb93a386Sopenharmony_ci return inverted * (2 * 8 * kStyleCnt * 2) + 1675cb93a386Sopenharmony_ci (int)dir * ( 8 * kStyleCnt * 2) + 1676cb93a386Sopenharmony_ci start * ( kStyleCnt * 2) + 1677cb93a386Sopenharmony_ci style * ( 2) + 1678cb93a386Sopenharmony_ci dash; 1679cb93a386Sopenharmony_ci }; 1680cb93a386Sopenharmony_ci static const SkPathDirection kSecondDirection = static_cast<SkPathDirection>(1); 1681cb93a386Sopenharmony_ci const int cnt = index(true, kSecondDirection, 7, static_cast<Style>(kStyleCnt - 1), true) + 1; 1682cb93a386Sopenharmony_ci SkAutoTArray<GrStyledShape> shapes(cnt); 1683cb93a386Sopenharmony_ci for (bool inverted : {false, true}) { 1684cb93a386Sopenharmony_ci for (SkPathDirection dir : {SkPathDirection::kCW, SkPathDirection::kCCW}) { 1685cb93a386Sopenharmony_ci for (unsigned start = 0; start < 8; ++start) { 1686cb93a386Sopenharmony_ci for (Style style : {kFill, kStroke, kHairline, kStrokeAndFill}) { 1687cb93a386Sopenharmony_ci for (bool dash : {false, true}) { 1688cb93a386Sopenharmony_ci sk_sp<SkPathEffect> pe = dash ? dashEffect : nullptr; 1689cb93a386Sopenharmony_ci shapes[index(inverted, dir, start, style, dash)] = 1690cb93a386Sopenharmony_ci GrStyledShape(rrect, dir, start, SkToBool(inverted), 1691cb93a386Sopenharmony_ci GrStyle(strokeRecs[style], std::move(pe))); 1692cb93a386Sopenharmony_ci } 1693cb93a386Sopenharmony_ci } 1694cb93a386Sopenharmony_ci } 1695cb93a386Sopenharmony_ci } 1696cb93a386Sopenharmony_ci } 1697cb93a386Sopenharmony_ci 1698cb93a386Sopenharmony_ci // Get the keys for some example shape instances that we'll use for comparision against the 1699cb93a386Sopenharmony_ci // rest. 1700cb93a386Sopenharmony_ci static constexpr SkPathDirection kExamplesDir = SkPathDirection::kCW; 1701cb93a386Sopenharmony_ci static constexpr unsigned kExamplesStart = 0; 1702cb93a386Sopenharmony_ci const GrStyledShape& exampleFillCase = shapes[index(false, kExamplesDir, kExamplesStart, kFill, 1703cb93a386Sopenharmony_ci false)]; 1704cb93a386Sopenharmony_ci Key exampleFillCaseKey; 1705cb93a386Sopenharmony_ci make_key(&exampleFillCaseKey, exampleFillCase); 1706cb93a386Sopenharmony_ci 1707cb93a386Sopenharmony_ci const GrStyledShape& exampleStrokeAndFillCase = shapes[index(false, kExamplesDir, 1708cb93a386Sopenharmony_ci kExamplesStart, kStrokeAndFill, false)]; 1709cb93a386Sopenharmony_ci Key exampleStrokeAndFillCaseKey; 1710cb93a386Sopenharmony_ci make_key(&exampleStrokeAndFillCaseKey, exampleStrokeAndFillCase); 1711cb93a386Sopenharmony_ci 1712cb93a386Sopenharmony_ci const GrStyledShape& exampleInvFillCase = shapes[index(true, kExamplesDir, 1713cb93a386Sopenharmony_ci kExamplesStart, kFill, false)]; 1714cb93a386Sopenharmony_ci Key exampleInvFillCaseKey; 1715cb93a386Sopenharmony_ci make_key(&exampleInvFillCaseKey, exampleInvFillCase); 1716cb93a386Sopenharmony_ci 1717cb93a386Sopenharmony_ci const GrStyledShape& exampleInvStrokeAndFillCase = shapes[index(true, kExamplesDir, 1718cb93a386Sopenharmony_ci kExamplesStart, kStrokeAndFill, 1719cb93a386Sopenharmony_ci false)]; 1720cb93a386Sopenharmony_ci Key exampleInvStrokeAndFillCaseKey; 1721cb93a386Sopenharmony_ci make_key(&exampleInvStrokeAndFillCaseKey, exampleInvStrokeAndFillCase); 1722cb93a386Sopenharmony_ci 1723cb93a386Sopenharmony_ci const GrStyledShape& exampleStrokeCase = shapes[index(false, kExamplesDir, kExamplesStart, 1724cb93a386Sopenharmony_ci kStroke, false)]; 1725cb93a386Sopenharmony_ci Key exampleStrokeCaseKey; 1726cb93a386Sopenharmony_ci make_key(&exampleStrokeCaseKey, exampleStrokeCase); 1727cb93a386Sopenharmony_ci 1728cb93a386Sopenharmony_ci const GrStyledShape& exampleInvStrokeCase = shapes[index(true, kExamplesDir, kExamplesStart, 1729cb93a386Sopenharmony_ci kStroke, false)]; 1730cb93a386Sopenharmony_ci Key exampleInvStrokeCaseKey; 1731cb93a386Sopenharmony_ci make_key(&exampleInvStrokeCaseKey, exampleInvStrokeCase); 1732cb93a386Sopenharmony_ci 1733cb93a386Sopenharmony_ci const GrStyledShape& exampleHairlineCase = shapes[index(false, kExamplesDir, kExamplesStart, 1734cb93a386Sopenharmony_ci kHairline, false)]; 1735cb93a386Sopenharmony_ci Key exampleHairlineCaseKey; 1736cb93a386Sopenharmony_ci make_key(&exampleHairlineCaseKey, exampleHairlineCase); 1737cb93a386Sopenharmony_ci 1738cb93a386Sopenharmony_ci const GrStyledShape& exampleInvHairlineCase = shapes[index(true, kExamplesDir, kExamplesStart, 1739cb93a386Sopenharmony_ci kHairline, false)]; 1740cb93a386Sopenharmony_ci Key exampleInvHairlineCaseKey; 1741cb93a386Sopenharmony_ci make_key(&exampleInvHairlineCaseKey, exampleInvHairlineCase); 1742cb93a386Sopenharmony_ci 1743cb93a386Sopenharmony_ci // These initializations suppress warnings. 1744cb93a386Sopenharmony_ci SkRRect queryRR = SkRRect::MakeEmpty(); 1745cb93a386Sopenharmony_ci SkPathDirection queryDir = SkPathDirection::kCW; 1746cb93a386Sopenharmony_ci unsigned queryStart = ~0U; 1747cb93a386Sopenharmony_ci bool queryInverted = true; 1748cb93a386Sopenharmony_ci 1749cb93a386Sopenharmony_ci REPORTER_ASSERT(r, exampleFillCase.asRRect(&queryRR, &queryDir, &queryStart, &queryInverted)); 1750cb93a386Sopenharmony_ci REPORTER_ASSERT(r, queryRR == rrect); 1751cb93a386Sopenharmony_ci REPORTER_ASSERT(r, SkPathDirection::kCW == queryDir); 1752cb93a386Sopenharmony_ci REPORTER_ASSERT(r, 0 == queryStart); 1753cb93a386Sopenharmony_ci REPORTER_ASSERT(r, !queryInverted); 1754cb93a386Sopenharmony_ci 1755cb93a386Sopenharmony_ci REPORTER_ASSERT(r, exampleInvFillCase.asRRect(&queryRR, &queryDir, &queryStart, 1756cb93a386Sopenharmony_ci &queryInverted)); 1757cb93a386Sopenharmony_ci REPORTER_ASSERT(r, queryRR == rrect); 1758cb93a386Sopenharmony_ci REPORTER_ASSERT(r, SkPathDirection::kCW == queryDir); 1759cb93a386Sopenharmony_ci REPORTER_ASSERT(r, 0 == queryStart); 1760cb93a386Sopenharmony_ci REPORTER_ASSERT(r, queryInverted); 1761cb93a386Sopenharmony_ci 1762cb93a386Sopenharmony_ci REPORTER_ASSERT(r, exampleStrokeAndFillCase.asRRect(&queryRR, &queryDir, &queryStart, 1763cb93a386Sopenharmony_ci &queryInverted)); 1764cb93a386Sopenharmony_ci REPORTER_ASSERT(r, queryRR == rrect); 1765cb93a386Sopenharmony_ci REPORTER_ASSERT(r, SkPathDirection::kCW == queryDir); 1766cb93a386Sopenharmony_ci REPORTER_ASSERT(r, 0 == queryStart); 1767cb93a386Sopenharmony_ci REPORTER_ASSERT(r, !queryInverted); 1768cb93a386Sopenharmony_ci 1769cb93a386Sopenharmony_ci REPORTER_ASSERT(r, exampleInvStrokeAndFillCase.asRRect(&queryRR, &queryDir, &queryStart, 1770cb93a386Sopenharmony_ci &queryInverted)); 1771cb93a386Sopenharmony_ci REPORTER_ASSERT(r, queryRR == rrect); 1772cb93a386Sopenharmony_ci REPORTER_ASSERT(r, SkPathDirection::kCW == queryDir); 1773cb93a386Sopenharmony_ci REPORTER_ASSERT(r, 0 == queryStart); 1774cb93a386Sopenharmony_ci REPORTER_ASSERT(r, queryInverted); 1775cb93a386Sopenharmony_ci 1776cb93a386Sopenharmony_ci REPORTER_ASSERT(r, exampleHairlineCase.asRRect(&queryRR, &queryDir, &queryStart, 1777cb93a386Sopenharmony_ci &queryInverted)); 1778cb93a386Sopenharmony_ci REPORTER_ASSERT(r, queryRR == rrect); 1779cb93a386Sopenharmony_ci REPORTER_ASSERT(r, SkPathDirection::kCW == queryDir); 1780cb93a386Sopenharmony_ci REPORTER_ASSERT(r, 0 == queryStart); 1781cb93a386Sopenharmony_ci REPORTER_ASSERT(r, !queryInverted); 1782cb93a386Sopenharmony_ci 1783cb93a386Sopenharmony_ci REPORTER_ASSERT(r, exampleInvHairlineCase.asRRect(&queryRR, &queryDir, &queryStart, 1784cb93a386Sopenharmony_ci &queryInverted)); 1785cb93a386Sopenharmony_ci REPORTER_ASSERT(r, queryRR == rrect); 1786cb93a386Sopenharmony_ci REPORTER_ASSERT(r, SkPathDirection::kCW == queryDir); 1787cb93a386Sopenharmony_ci REPORTER_ASSERT(r, 0 == queryStart); 1788cb93a386Sopenharmony_ci REPORTER_ASSERT(r, queryInverted); 1789cb93a386Sopenharmony_ci 1790cb93a386Sopenharmony_ci REPORTER_ASSERT(r, exampleStrokeCase.asRRect(&queryRR, &queryDir, &queryStart, &queryInverted)); 1791cb93a386Sopenharmony_ci REPORTER_ASSERT(r, queryRR == rrect); 1792cb93a386Sopenharmony_ci REPORTER_ASSERT(r, SkPathDirection::kCW == queryDir); 1793cb93a386Sopenharmony_ci REPORTER_ASSERT(r, 0 == queryStart); 1794cb93a386Sopenharmony_ci REPORTER_ASSERT(r, !queryInverted); 1795cb93a386Sopenharmony_ci 1796cb93a386Sopenharmony_ci REPORTER_ASSERT(r, exampleInvStrokeCase.asRRect(&queryRR, &queryDir, &queryStart, 1797cb93a386Sopenharmony_ci &queryInverted)); 1798cb93a386Sopenharmony_ci REPORTER_ASSERT(r, queryRR == rrect); 1799cb93a386Sopenharmony_ci REPORTER_ASSERT(r, SkPathDirection::kCW == queryDir); 1800cb93a386Sopenharmony_ci REPORTER_ASSERT(r, 0 == queryStart); 1801cb93a386Sopenharmony_ci REPORTER_ASSERT(r, queryInverted); 1802cb93a386Sopenharmony_ci 1803cb93a386Sopenharmony_ci // Remember that the key reflects the geometry before styling is applied. 1804cb93a386Sopenharmony_ci REPORTER_ASSERT(r, exampleFillCaseKey != exampleInvFillCaseKey); 1805cb93a386Sopenharmony_ci REPORTER_ASSERT(r, exampleFillCaseKey == exampleStrokeAndFillCaseKey); 1806cb93a386Sopenharmony_ci REPORTER_ASSERT(r, exampleFillCaseKey != exampleInvStrokeAndFillCaseKey); 1807cb93a386Sopenharmony_ci REPORTER_ASSERT(r, exampleFillCaseKey == exampleStrokeCaseKey); 1808cb93a386Sopenharmony_ci REPORTER_ASSERT(r, exampleFillCaseKey != exampleInvStrokeCaseKey); 1809cb93a386Sopenharmony_ci REPORTER_ASSERT(r, exampleFillCaseKey == exampleHairlineCaseKey); 1810cb93a386Sopenharmony_ci REPORTER_ASSERT(r, exampleFillCaseKey != exampleInvHairlineCaseKey); 1811cb93a386Sopenharmony_ci REPORTER_ASSERT(r, exampleInvStrokeAndFillCaseKey == exampleInvFillCaseKey); 1812cb93a386Sopenharmony_ci REPORTER_ASSERT(r, exampleInvStrokeAndFillCaseKey == exampleInvStrokeCaseKey); 1813cb93a386Sopenharmony_ci REPORTER_ASSERT(r, exampleInvStrokeAndFillCaseKey == exampleInvHairlineCaseKey); 1814cb93a386Sopenharmony_ci 1815cb93a386Sopenharmony_ci for (bool inverted : {false, true}) { 1816cb93a386Sopenharmony_ci for (SkPathDirection dir : {SkPathDirection::kCW, SkPathDirection::kCCW}) { 1817cb93a386Sopenharmony_ci for (unsigned start = 0; start < 8; ++start) { 1818cb93a386Sopenharmony_ci for (bool dash : {false, true}) { 1819cb93a386Sopenharmony_ci const GrStyledShape& fillCase = shapes[index(inverted, dir, start, kFill, 1820cb93a386Sopenharmony_ci dash)]; 1821cb93a386Sopenharmony_ci Key fillCaseKey; 1822cb93a386Sopenharmony_ci make_key(&fillCaseKey, fillCase); 1823cb93a386Sopenharmony_ci 1824cb93a386Sopenharmony_ci const GrStyledShape& strokeAndFillCase = shapes[index(inverted, dir, start, 1825cb93a386Sopenharmony_ci kStrokeAndFill, dash)]; 1826cb93a386Sopenharmony_ci Key strokeAndFillCaseKey; 1827cb93a386Sopenharmony_ci make_key(&strokeAndFillCaseKey, strokeAndFillCase); 1828cb93a386Sopenharmony_ci 1829cb93a386Sopenharmony_ci // Both fill and stroke-and-fill shapes must respect the inverseness and both 1830cb93a386Sopenharmony_ci // ignore dashing. 1831cb93a386Sopenharmony_ci REPORTER_ASSERT(r, !fillCase.style().pathEffect()); 1832cb93a386Sopenharmony_ci REPORTER_ASSERT(r, !strokeAndFillCase.style().pathEffect()); 1833cb93a386Sopenharmony_ci TestCase a(fillCase, r); 1834cb93a386Sopenharmony_ci TestCase b(inverted ? exampleInvFillCase : exampleFillCase, r); 1835cb93a386Sopenharmony_ci TestCase c(strokeAndFillCase, r); 1836cb93a386Sopenharmony_ci TestCase d(inverted ? exampleInvStrokeAndFillCase 1837cb93a386Sopenharmony_ci : exampleStrokeAndFillCase, r); 1838cb93a386Sopenharmony_ci a.compare(r, b, TestCase::kAllSame_ComparisonExpecation); 1839cb93a386Sopenharmony_ci c.compare(r, d, TestCase::kAllSame_ComparisonExpecation); 1840cb93a386Sopenharmony_ci 1841cb93a386Sopenharmony_ci const GrStyledShape& strokeCase = shapes[index(inverted, dir, start, kStroke, 1842cb93a386Sopenharmony_ci dash)]; 1843cb93a386Sopenharmony_ci const GrStyledShape& hairlineCase = shapes[index(inverted, dir, start, 1844cb93a386Sopenharmony_ci kHairline, dash)]; 1845cb93a386Sopenharmony_ci 1846cb93a386Sopenharmony_ci TestCase e(strokeCase, r); 1847cb93a386Sopenharmony_ci TestCase g(hairlineCase, r); 1848cb93a386Sopenharmony_ci 1849cb93a386Sopenharmony_ci // Both hairline and stroke shapes must respect the dashing. 1850cb93a386Sopenharmony_ci if (dash) { 1851cb93a386Sopenharmony_ci // Dashing always ignores the inverseness. skbug.com/5421 1852cb93a386Sopenharmony_ci TestCase f(exampleStrokeCase, r); 1853cb93a386Sopenharmony_ci TestCase h(exampleHairlineCase, r); 1854cb93a386Sopenharmony_ci unsigned expectedStart = canonicalize_rrect_start(start, rrect); 1855cb93a386Sopenharmony_ci REPORTER_ASSERT(r, strokeCase.style().pathEffect()); 1856cb93a386Sopenharmony_ci REPORTER_ASSERT(r, hairlineCase.style().pathEffect()); 1857cb93a386Sopenharmony_ci 1858cb93a386Sopenharmony_ci REPORTER_ASSERT(r, strokeCase.asRRect(&queryRR, &queryDir, &queryStart, 1859cb93a386Sopenharmony_ci &queryInverted)); 1860cb93a386Sopenharmony_ci REPORTER_ASSERT(r, queryRR == rrect); 1861cb93a386Sopenharmony_ci REPORTER_ASSERT(r, queryDir == dir); 1862cb93a386Sopenharmony_ci REPORTER_ASSERT(r, queryStart == expectedStart); 1863cb93a386Sopenharmony_ci REPORTER_ASSERT(r, !queryInverted); 1864cb93a386Sopenharmony_ci REPORTER_ASSERT(r, hairlineCase.asRRect(&queryRR, &queryDir, &queryStart, 1865cb93a386Sopenharmony_ci &queryInverted)); 1866cb93a386Sopenharmony_ci REPORTER_ASSERT(r, queryRR == rrect); 1867cb93a386Sopenharmony_ci REPORTER_ASSERT(r, queryDir == dir); 1868cb93a386Sopenharmony_ci REPORTER_ASSERT(r, queryStart == expectedStart); 1869cb93a386Sopenharmony_ci REPORTER_ASSERT(r, !queryInverted); 1870cb93a386Sopenharmony_ci 1871cb93a386Sopenharmony_ci // The pre-style case for the dash will match the non-dash example iff the 1872cb93a386Sopenharmony_ci // dir and start match (dir=cw, start=0). 1873cb93a386Sopenharmony_ci if (0 == expectedStart && SkPathDirection::kCW == dir) { 1874cb93a386Sopenharmony_ci e.compare(r, f, TestCase::kSameUpToPE_ComparisonExpecation); 1875cb93a386Sopenharmony_ci g.compare(r, h, TestCase::kSameUpToPE_ComparisonExpecation); 1876cb93a386Sopenharmony_ci } else { 1877cb93a386Sopenharmony_ci e.compare(r, f, TestCase::kAllDifferent_ComparisonExpecation); 1878cb93a386Sopenharmony_ci g.compare(r, h, TestCase::kAllDifferent_ComparisonExpecation); 1879cb93a386Sopenharmony_ci } 1880cb93a386Sopenharmony_ci } else { 1881cb93a386Sopenharmony_ci TestCase f(inverted ? exampleInvStrokeCase : exampleStrokeCase, r); 1882cb93a386Sopenharmony_ci TestCase h(inverted ? exampleInvHairlineCase : exampleHairlineCase, r); 1883cb93a386Sopenharmony_ci REPORTER_ASSERT(r, !strokeCase.style().pathEffect()); 1884cb93a386Sopenharmony_ci REPORTER_ASSERT(r, !hairlineCase.style().pathEffect()); 1885cb93a386Sopenharmony_ci e.compare(r, f, TestCase::kAllSame_ComparisonExpecation); 1886cb93a386Sopenharmony_ci g.compare(r, h, TestCase::kAllSame_ComparisonExpecation); 1887cb93a386Sopenharmony_ci } 1888cb93a386Sopenharmony_ci } 1889cb93a386Sopenharmony_ci } 1890cb93a386Sopenharmony_ci } 1891cb93a386Sopenharmony_ci } 1892cb93a386Sopenharmony_ci} 1893cb93a386Sopenharmony_ci 1894cb93a386Sopenharmony_ciDEF_TEST(GrStyledShape_lines, r) { 1895cb93a386Sopenharmony_ci static constexpr SkPoint kA { 1, 1}; 1896cb93a386Sopenharmony_ci static constexpr SkPoint kB { 5, -9}; 1897cb93a386Sopenharmony_ci static constexpr SkPoint kC {-3, 17}; 1898cb93a386Sopenharmony_ci 1899cb93a386Sopenharmony_ci SkPath lineAB = SkPath::Line(kA, kB); 1900cb93a386Sopenharmony_ci SkPath lineBA = SkPath::Line(kB, kA); 1901cb93a386Sopenharmony_ci SkPath lineAC = SkPath::Line(kB, kC); 1902cb93a386Sopenharmony_ci SkPath invLineAB = lineAB; 1903cb93a386Sopenharmony_ci 1904cb93a386Sopenharmony_ci invLineAB.setFillType(SkPathFillType::kInverseEvenOdd); 1905cb93a386Sopenharmony_ci 1906cb93a386Sopenharmony_ci SkPaint fill; 1907cb93a386Sopenharmony_ci SkPaint stroke; 1908cb93a386Sopenharmony_ci stroke.setStyle(SkPaint::kStroke_Style); 1909cb93a386Sopenharmony_ci stroke.setStrokeWidth(2.f); 1910cb93a386Sopenharmony_ci SkPaint hairline; 1911cb93a386Sopenharmony_ci hairline.setStyle(SkPaint::kStroke_Style); 1912cb93a386Sopenharmony_ci hairline.setStrokeWidth(0.f); 1913cb93a386Sopenharmony_ci SkPaint dash = stroke; 1914cb93a386Sopenharmony_ci dash.setPathEffect(make_dash()); 1915cb93a386Sopenharmony_ci 1916cb93a386Sopenharmony_ci TestCase fillAB(r, lineAB, fill); 1917cb93a386Sopenharmony_ci TestCase fillEmpty(r, SkPath(), fill); 1918cb93a386Sopenharmony_ci fillAB.compare(r, fillEmpty, TestCase::kAllSame_ComparisonExpecation); 1919cb93a386Sopenharmony_ci REPORTER_ASSERT(r, !fillAB.baseShape().asLine(nullptr, nullptr)); 1920cb93a386Sopenharmony_ci 1921cb93a386Sopenharmony_ci SkPath path; 1922cb93a386Sopenharmony_ci path.toggleInverseFillType(); 1923cb93a386Sopenharmony_ci TestCase fillEmptyInverted(r, path, fill); 1924cb93a386Sopenharmony_ci TestCase fillABInverted(r, invLineAB, fill); 1925cb93a386Sopenharmony_ci fillABInverted.compare(r, fillEmptyInverted, TestCase::kAllSame_ComparisonExpecation); 1926cb93a386Sopenharmony_ci REPORTER_ASSERT(r, !fillABInverted.baseShape().asLine(nullptr, nullptr)); 1927cb93a386Sopenharmony_ci 1928cb93a386Sopenharmony_ci TestCase strokeAB(r, lineAB, stroke); 1929cb93a386Sopenharmony_ci TestCase strokeBA(r, lineBA, stroke); 1930cb93a386Sopenharmony_ci TestCase strokeAC(r, lineAC, stroke); 1931cb93a386Sopenharmony_ci 1932cb93a386Sopenharmony_ci TestCase hairlineAB(r, lineAB, hairline); 1933cb93a386Sopenharmony_ci TestCase hairlineBA(r, lineBA, hairline); 1934cb93a386Sopenharmony_ci TestCase hairlineAC(r, lineAC, hairline); 1935cb93a386Sopenharmony_ci 1936cb93a386Sopenharmony_ci TestCase dashAB(r, lineAB, dash); 1937cb93a386Sopenharmony_ci TestCase dashBA(r, lineBA, dash); 1938cb93a386Sopenharmony_ci TestCase dashAC(r, lineAC, dash); 1939cb93a386Sopenharmony_ci 1940cb93a386Sopenharmony_ci strokeAB.compare(r, fillAB, TestCase::kAllDifferent_ComparisonExpecation); 1941cb93a386Sopenharmony_ci 1942cb93a386Sopenharmony_ci strokeAB.compare(r, strokeBA, TestCase::kAllSame_ComparisonExpecation); 1943cb93a386Sopenharmony_ci strokeAB.compare(r, strokeAC, TestCase::kAllDifferent_ComparisonExpecation); 1944cb93a386Sopenharmony_ci 1945cb93a386Sopenharmony_ci hairlineAB.compare(r, hairlineBA, TestCase::kAllSame_ComparisonExpecation); 1946cb93a386Sopenharmony_ci hairlineAB.compare(r, hairlineAC, TestCase::kAllDifferent_ComparisonExpecation); 1947cb93a386Sopenharmony_ci 1948cb93a386Sopenharmony_ci dashAB.compare(r, dashBA, TestCase::kAllDifferent_ComparisonExpecation); 1949cb93a386Sopenharmony_ci dashAB.compare(r, dashAC, TestCase::kAllDifferent_ComparisonExpecation); 1950cb93a386Sopenharmony_ci 1951cb93a386Sopenharmony_ci strokeAB.compare(r, hairlineAB, TestCase::kSameUpToStroke_ComparisonExpecation); 1952cb93a386Sopenharmony_ci 1953cb93a386Sopenharmony_ci // One of dashAB or dashBA should have the same line as strokeAB. It depends upon how 1954cb93a386Sopenharmony_ci // GrStyledShape canonicalizes line endpoints (when it can, i.e. when not dashed). 1955cb93a386Sopenharmony_ci bool canonicalizeAsAB; 1956cb93a386Sopenharmony_ci SkPoint canonicalPts[2] {kA, kB}; 1957cb93a386Sopenharmony_ci // Init these to suppress warnings. 1958cb93a386Sopenharmony_ci bool inverted = true; 1959cb93a386Sopenharmony_ci SkPoint pts[2] {{0, 0}, {0, 0}}; 1960cb93a386Sopenharmony_ci REPORTER_ASSERT(r, strokeAB.baseShape().asLine(pts, &inverted) && !inverted); 1961cb93a386Sopenharmony_ci if (pts[0] == kA && pts[1] == kB) { 1962cb93a386Sopenharmony_ci canonicalizeAsAB = true; 1963cb93a386Sopenharmony_ci } else if (pts[1] == kA && pts[0] == kB) { 1964cb93a386Sopenharmony_ci canonicalizeAsAB = false; 1965cb93a386Sopenharmony_ci using std::swap; 1966cb93a386Sopenharmony_ci swap(canonicalPts[0], canonicalPts[1]); 1967cb93a386Sopenharmony_ci } else { 1968cb93a386Sopenharmony_ci ERRORF(r, "Should return pts (a,b) or (b, a)"); 1969cb93a386Sopenharmony_ci return; 1970cb93a386Sopenharmony_ci } 1971cb93a386Sopenharmony_ci 1972cb93a386Sopenharmony_ci strokeAB.compare(r, canonicalizeAsAB ? dashAB : dashBA, 1973cb93a386Sopenharmony_ci TestCase::kSameUpToPE_ComparisonExpecation); 1974cb93a386Sopenharmony_ci REPORTER_ASSERT(r, strokeAB.baseShape().asLine(pts, &inverted) && !inverted && 1975cb93a386Sopenharmony_ci pts[0] == canonicalPts[0] && pts[1] == canonicalPts[1]); 1976cb93a386Sopenharmony_ci REPORTER_ASSERT(r, hairlineAB.baseShape().asLine(pts, &inverted) && !inverted && 1977cb93a386Sopenharmony_ci pts[0] == canonicalPts[0] && pts[1] == canonicalPts[1]); 1978cb93a386Sopenharmony_ci REPORTER_ASSERT(r, dashAB.baseShape().asLine(pts, &inverted) && !inverted && 1979cb93a386Sopenharmony_ci pts[0] == kA && pts[1] == kB); 1980cb93a386Sopenharmony_ci REPORTER_ASSERT(r, dashBA.baseShape().asLine(pts, &inverted) && !inverted && 1981cb93a386Sopenharmony_ci pts[0] == kB && pts[1] == kA); 1982cb93a386Sopenharmony_ci 1983cb93a386Sopenharmony_ci 1984cb93a386Sopenharmony_ci TestCase strokeInvAB(r, invLineAB, stroke); 1985cb93a386Sopenharmony_ci TestCase hairlineInvAB(r, invLineAB, hairline); 1986cb93a386Sopenharmony_ci TestCase dashInvAB(r, invLineAB, dash); 1987cb93a386Sopenharmony_ci strokeInvAB.compare(r, strokeAB, TestCase::kAllDifferent_ComparisonExpecation); 1988cb93a386Sopenharmony_ci hairlineInvAB.compare(r, hairlineAB, TestCase::kAllDifferent_ComparisonExpecation); 1989cb93a386Sopenharmony_ci // Dashing ignores inverse. 1990cb93a386Sopenharmony_ci dashInvAB.compare(r, dashAB, TestCase::kAllSame_ComparisonExpecation); 1991cb93a386Sopenharmony_ci 1992cb93a386Sopenharmony_ci REPORTER_ASSERT(r, strokeInvAB.baseShape().asLine(pts, &inverted) && inverted && 1993cb93a386Sopenharmony_ci pts[0] == canonicalPts[0] && pts[1] == canonicalPts[1]); 1994cb93a386Sopenharmony_ci REPORTER_ASSERT(r, hairlineInvAB.baseShape().asLine(pts, &inverted) && inverted && 1995cb93a386Sopenharmony_ci pts[0] == canonicalPts[0] && pts[1] == canonicalPts[1]); 1996cb93a386Sopenharmony_ci // Dashing ignores inverse. 1997cb93a386Sopenharmony_ci REPORTER_ASSERT(r, dashInvAB.baseShape().asLine(pts, &inverted) && !inverted && 1998cb93a386Sopenharmony_ci pts[0] == kA && pts[1] == kB); 1999cb93a386Sopenharmony_ci 2000cb93a386Sopenharmony_ci} 2001cb93a386Sopenharmony_ci 2002cb93a386Sopenharmony_ciDEF_TEST(GrStyledShape_stroked_lines, r) { 2003cb93a386Sopenharmony_ci static constexpr SkScalar kIntervals1[] = {1.f, 0.f}; 2004cb93a386Sopenharmony_ci auto dash1 = SkDashPathEffect::Make(kIntervals1, SK_ARRAY_COUNT(kIntervals1), 0.f); 2005cb93a386Sopenharmony_ci REPORTER_ASSERT(r, dash1); 2006cb93a386Sopenharmony_ci static constexpr SkScalar kIntervals2[] = {10.f, 0.f, 5.f, 0.f}; 2007cb93a386Sopenharmony_ci auto dash2 = SkDashPathEffect::Make(kIntervals2, SK_ARRAY_COUNT(kIntervals2), 10.f); 2008cb93a386Sopenharmony_ci REPORTER_ASSERT(r, dash2); 2009cb93a386Sopenharmony_ci 2010cb93a386Sopenharmony_ci sk_sp<SkPathEffect> pathEffects[] = {nullptr, std::move(dash1), std::move(dash2)}; 2011cb93a386Sopenharmony_ci 2012cb93a386Sopenharmony_ci for (const auto& pe : pathEffects) { 2013cb93a386Sopenharmony_ci // Paints to try 2014cb93a386Sopenharmony_ci SkPaint buttCap; 2015cb93a386Sopenharmony_ci buttCap.setStyle(SkPaint::kStroke_Style); 2016cb93a386Sopenharmony_ci buttCap.setStrokeWidth(4); 2017cb93a386Sopenharmony_ci buttCap.setStrokeCap(SkPaint::kButt_Cap); 2018cb93a386Sopenharmony_ci buttCap.setPathEffect(pe); 2019cb93a386Sopenharmony_ci 2020cb93a386Sopenharmony_ci SkPaint squareCap = buttCap; 2021cb93a386Sopenharmony_ci squareCap.setStrokeCap(SkPaint::kSquare_Cap); 2022cb93a386Sopenharmony_ci squareCap.setPathEffect(pe); 2023cb93a386Sopenharmony_ci 2024cb93a386Sopenharmony_ci SkPaint roundCap = buttCap; 2025cb93a386Sopenharmony_ci roundCap.setStrokeCap(SkPaint::kRound_Cap); 2026cb93a386Sopenharmony_ci roundCap.setPathEffect(pe); 2027cb93a386Sopenharmony_ci 2028cb93a386Sopenharmony_ci // vertical 2029cb93a386Sopenharmony_ci SkPath linePath; 2030cb93a386Sopenharmony_ci linePath.moveTo(4, 4); 2031cb93a386Sopenharmony_ci linePath.lineTo(4, 5); 2032cb93a386Sopenharmony_ci 2033cb93a386Sopenharmony_ci SkPaint fill; 2034cb93a386Sopenharmony_ci 2035cb93a386Sopenharmony_ci make_TestCase(r, linePath, buttCap)->compare( 2036cb93a386Sopenharmony_ci r, TestCase(r, SkRect::MakeLTRB(2, 4, 6, 5), fill), 2037cb93a386Sopenharmony_ci TestCase::kAllSame_ComparisonExpecation); 2038cb93a386Sopenharmony_ci 2039cb93a386Sopenharmony_ci make_TestCase(r, linePath, squareCap)->compare( 2040cb93a386Sopenharmony_ci r, TestCase(r, SkRect::MakeLTRB(2, 2, 6, 7), fill), 2041cb93a386Sopenharmony_ci TestCase::kAllSame_ComparisonExpecation); 2042cb93a386Sopenharmony_ci 2043cb93a386Sopenharmony_ci make_TestCase(r, linePath, roundCap)->compare(r, 2044cb93a386Sopenharmony_ci TestCase(r, SkRRect::MakeRectXY(SkRect::MakeLTRB(2, 2, 6, 7), 2, 2), fill), 2045cb93a386Sopenharmony_ci TestCase::kAllSame_ComparisonExpecation); 2046cb93a386Sopenharmony_ci 2047cb93a386Sopenharmony_ci // horizontal 2048cb93a386Sopenharmony_ci linePath.reset(); 2049cb93a386Sopenharmony_ci linePath.moveTo(4, 4); 2050cb93a386Sopenharmony_ci linePath.lineTo(5, 4); 2051cb93a386Sopenharmony_ci 2052cb93a386Sopenharmony_ci make_TestCase(r, linePath, buttCap)->compare( 2053cb93a386Sopenharmony_ci r, TestCase(r, SkRect::MakeLTRB(4, 2, 5, 6), fill), 2054cb93a386Sopenharmony_ci TestCase::kAllSame_ComparisonExpecation); 2055cb93a386Sopenharmony_ci make_TestCase(r, linePath, squareCap)->compare( 2056cb93a386Sopenharmony_ci r, TestCase(r, SkRect::MakeLTRB(2, 2, 7, 6), fill), 2057cb93a386Sopenharmony_ci TestCase::kAllSame_ComparisonExpecation); 2058cb93a386Sopenharmony_ci make_TestCase(r, linePath, roundCap)->compare( 2059cb93a386Sopenharmony_ci r, TestCase(r, SkRRect::MakeRectXY(SkRect::MakeLTRB(2, 2, 7, 6), 2, 2), fill), 2060cb93a386Sopenharmony_ci TestCase::kAllSame_ComparisonExpecation); 2061cb93a386Sopenharmony_ci 2062cb93a386Sopenharmony_ci // point 2063cb93a386Sopenharmony_ci linePath.reset(); 2064cb93a386Sopenharmony_ci linePath.moveTo(4, 4); 2065cb93a386Sopenharmony_ci linePath.lineTo(4, 4); 2066cb93a386Sopenharmony_ci 2067cb93a386Sopenharmony_ci make_TestCase(r, linePath, buttCap)->compare( 2068cb93a386Sopenharmony_ci r, TestCase(r, SkRect::MakeEmpty(), fill), 2069cb93a386Sopenharmony_ci TestCase::kAllSame_ComparisonExpecation); 2070cb93a386Sopenharmony_ci make_TestCase(r, linePath, squareCap)->compare( 2071cb93a386Sopenharmony_ci r, TestCase(r, SkRect::MakeLTRB(2, 2, 6, 6), fill), 2072cb93a386Sopenharmony_ci TestCase::kAllSame_ComparisonExpecation); 2073cb93a386Sopenharmony_ci make_TestCase(r, linePath, roundCap)->compare( 2074cb93a386Sopenharmony_ci r, TestCase(r, SkRRect::MakeRectXY(SkRect::MakeLTRB(2, 2, 6, 6), 2, 2), fill), 2075cb93a386Sopenharmony_ci TestCase::kAllSame_ComparisonExpecation); 2076cb93a386Sopenharmony_ci } 2077cb93a386Sopenharmony_ci} 2078cb93a386Sopenharmony_ci 2079cb93a386Sopenharmony_ciDEF_TEST(GrStyledShape_short_path_keys, r) { 2080cb93a386Sopenharmony_ci SkPaint paints[4]; 2081cb93a386Sopenharmony_ci paints[1].setStyle(SkPaint::kStroke_Style); 2082cb93a386Sopenharmony_ci paints[1].setStrokeWidth(5.f); 2083cb93a386Sopenharmony_ci paints[2].setStyle(SkPaint::kStroke_Style); 2084cb93a386Sopenharmony_ci paints[2].setStrokeWidth(0.f); 2085cb93a386Sopenharmony_ci paints[3].setStyle(SkPaint::kStrokeAndFill_Style); 2086cb93a386Sopenharmony_ci paints[3].setStrokeWidth(5.f); 2087cb93a386Sopenharmony_ci 2088cb93a386Sopenharmony_ci auto compare = [r, &paints] (const SkPath& pathA, const SkPath& pathB, 2089cb93a386Sopenharmony_ci TestCase::ComparisonExpecation expectation) { 2090cb93a386Sopenharmony_ci SkPath volatileA = pathA; 2091cb93a386Sopenharmony_ci SkPath volatileB = pathB; 2092cb93a386Sopenharmony_ci volatileA.setIsVolatile(true); 2093cb93a386Sopenharmony_ci volatileB.setIsVolatile(true); 2094cb93a386Sopenharmony_ci for (const SkPaint& paint : paints) { 2095cb93a386Sopenharmony_ci REPORTER_ASSERT(r, !GrStyledShape(volatileA, paint).hasUnstyledKey()); 2096cb93a386Sopenharmony_ci REPORTER_ASSERT(r, !GrStyledShape(volatileB, paint).hasUnstyledKey()); 2097cb93a386Sopenharmony_ci for (PathGeo::Invert invert : {PathGeo::Invert::kNo, PathGeo::Invert::kYes}) { 2098cb93a386Sopenharmony_ci TestCase caseA(PathGeo(pathA, invert), paint, r); 2099cb93a386Sopenharmony_ci TestCase caseB(PathGeo(pathB, invert), paint, r); 2100cb93a386Sopenharmony_ci caseA.compare(r, caseB, expectation); 2101cb93a386Sopenharmony_ci } 2102cb93a386Sopenharmony_ci } 2103cb93a386Sopenharmony_ci }; 2104cb93a386Sopenharmony_ci 2105cb93a386Sopenharmony_ci SkPath pathA; 2106cb93a386Sopenharmony_ci SkPath pathB; 2107cb93a386Sopenharmony_ci 2108cb93a386Sopenharmony_ci // Two identical paths 2109cb93a386Sopenharmony_ci pathA.lineTo(10.f, 10.f); 2110cb93a386Sopenharmony_ci pathA.conicTo(20.f, 20.f, 20.f, 30.f, 0.7f); 2111cb93a386Sopenharmony_ci 2112cb93a386Sopenharmony_ci pathB.lineTo(10.f, 10.f); 2113cb93a386Sopenharmony_ci pathB.conicTo(20.f, 20.f, 20.f, 30.f, 0.7f); 2114cb93a386Sopenharmony_ci compare(pathA, pathB, TestCase::kAllSame_ComparisonExpecation); 2115cb93a386Sopenharmony_ci 2116cb93a386Sopenharmony_ci // Give path b a different point 2117cb93a386Sopenharmony_ci pathB.reset(); 2118cb93a386Sopenharmony_ci pathB.lineTo(10.f, 10.f); 2119cb93a386Sopenharmony_ci pathB.conicTo(21.f, 20.f, 20.f, 30.f, 0.7f); 2120cb93a386Sopenharmony_ci compare(pathA, pathB, TestCase::kAllDifferent_ComparisonExpecation); 2121cb93a386Sopenharmony_ci 2122cb93a386Sopenharmony_ci // Give path b a different conic weight 2123cb93a386Sopenharmony_ci pathB.reset(); 2124cb93a386Sopenharmony_ci pathB.lineTo(10.f, 10.f); 2125cb93a386Sopenharmony_ci pathB.conicTo(20.f, 20.f, 20.f, 30.f, 0.6f); 2126cb93a386Sopenharmony_ci compare(pathA, pathB, TestCase::kAllDifferent_ComparisonExpecation); 2127cb93a386Sopenharmony_ci 2128cb93a386Sopenharmony_ci // Give path b an extra lineTo verb 2129cb93a386Sopenharmony_ci pathB.reset(); 2130cb93a386Sopenharmony_ci pathB.lineTo(10.f, 10.f); 2131cb93a386Sopenharmony_ci pathB.conicTo(20.f, 20.f, 20.f, 30.f, 0.6f); 2132cb93a386Sopenharmony_ci pathB.lineTo(50.f, 50.f); 2133cb93a386Sopenharmony_ci compare(pathA, pathB, TestCase::kAllDifferent_ComparisonExpecation); 2134cb93a386Sopenharmony_ci 2135cb93a386Sopenharmony_ci // Give path b a close 2136cb93a386Sopenharmony_ci pathB.reset(); 2137cb93a386Sopenharmony_ci pathB.lineTo(10.f, 10.f); 2138cb93a386Sopenharmony_ci pathB.conicTo(20.f, 20.f, 20.f, 30.f, 0.7f); 2139cb93a386Sopenharmony_ci pathB.close(); 2140cb93a386Sopenharmony_ci compare(pathA, pathB, TestCase::kAllDifferent_ComparisonExpecation); 2141cb93a386Sopenharmony_ci} 2142cb93a386Sopenharmony_ci 2143cb93a386Sopenharmony_ciDEF_TEST(GrStyledShape, reporter) { 2144cb93a386Sopenharmony_ci SkTArray<std::unique_ptr<Geo>> geos; 2145cb93a386Sopenharmony_ci SkTArray<std::unique_ptr<RRectPathGeo>> rrectPathGeos; 2146cb93a386Sopenharmony_ci 2147cb93a386Sopenharmony_ci for (auto r : { SkRect::MakeWH(10, 20), 2148cb93a386Sopenharmony_ci SkRect::MakeWH(-10, -20), 2149cb93a386Sopenharmony_ci SkRect::MakeWH(-10, 20), 2150cb93a386Sopenharmony_ci SkRect::MakeWH(10, -20)}) { 2151cb93a386Sopenharmony_ci geos.emplace_back(new RectGeo(r)); 2152cb93a386Sopenharmony_ci SkPath rectPath; 2153cb93a386Sopenharmony_ci rectPath.addRect(r); 2154cb93a386Sopenharmony_ci geos.emplace_back(new RRectPathGeo(rectPath, r, RRectPathGeo::RRectForStroke::kYes, 2155cb93a386Sopenharmony_ci PathGeo::Invert::kNo)); 2156cb93a386Sopenharmony_ci geos.emplace_back(new RRectPathGeo(rectPath, r, RRectPathGeo::RRectForStroke::kYes, 2157cb93a386Sopenharmony_ci PathGeo::Invert::kYes)); 2158cb93a386Sopenharmony_ci rrectPathGeos.emplace_back(new RRectPathGeo(rectPath, r, RRectPathGeo::RRectForStroke::kYes, 2159cb93a386Sopenharmony_ci PathGeo::Invert::kNo)); 2160cb93a386Sopenharmony_ci } 2161cb93a386Sopenharmony_ci for (auto rr : { SkRRect::MakeRect(SkRect::MakeWH(10, 10)), 2162cb93a386Sopenharmony_ci SkRRect::MakeRectXY(SkRect::MakeWH(10, 10), 3, 4), 2163cb93a386Sopenharmony_ci SkRRect::MakeOval(SkRect::MakeWH(20, 20))}) { 2164cb93a386Sopenharmony_ci geos.emplace_back(new RRectGeo(rr)); 2165cb93a386Sopenharmony_ci test_rrect(reporter, rr); 2166cb93a386Sopenharmony_ci SkPath rectPath; 2167cb93a386Sopenharmony_ci rectPath.addRRect(rr); 2168cb93a386Sopenharmony_ci geos.emplace_back(new RRectPathGeo(rectPath, rr, RRectPathGeo::RRectForStroke::kYes, 2169cb93a386Sopenharmony_ci PathGeo::Invert::kNo)); 2170cb93a386Sopenharmony_ci geos.emplace_back(new RRectPathGeo(rectPath, rr, RRectPathGeo::RRectForStroke::kYes, 2171cb93a386Sopenharmony_ci PathGeo::Invert::kYes)); 2172cb93a386Sopenharmony_ci rrectPathGeos.emplace_back(new RRectPathGeo(rectPath, rr, 2173cb93a386Sopenharmony_ci RRectPathGeo::RRectForStroke::kYes, 2174cb93a386Sopenharmony_ci PathGeo::Invert::kNo)); 2175cb93a386Sopenharmony_ci } 2176cb93a386Sopenharmony_ci 2177cb93a386Sopenharmony_ci // Arcs 2178cb93a386Sopenharmony_ci geos.emplace_back(new ArcGeo(SkRect::MakeWH(200, 100), 12.f, 110.f, false)); 2179cb93a386Sopenharmony_ci geos.emplace_back(new ArcGeo(SkRect::MakeWH(200, 100), 12.f, 110.f, true)); 2180cb93a386Sopenharmony_ci 2181cb93a386Sopenharmony_ci { 2182cb93a386Sopenharmony_ci SkPath openRectPath; 2183cb93a386Sopenharmony_ci openRectPath.moveTo(0, 0); 2184cb93a386Sopenharmony_ci openRectPath.lineTo(10, 0); 2185cb93a386Sopenharmony_ci openRectPath.lineTo(10, 10); 2186cb93a386Sopenharmony_ci openRectPath.lineTo(0, 10); 2187cb93a386Sopenharmony_ci geos.emplace_back(new RRectPathGeo( 2188cb93a386Sopenharmony_ci openRectPath, SkRect::MakeWH(10, 10), 2189cb93a386Sopenharmony_ci RRectPathGeo::RRectForStroke::kNo, PathGeo::Invert::kNo)); 2190cb93a386Sopenharmony_ci geos.emplace_back(new RRectPathGeo( 2191cb93a386Sopenharmony_ci openRectPath, SkRect::MakeWH(10, 10), 2192cb93a386Sopenharmony_ci RRectPathGeo::RRectForStroke::kNo, PathGeo::Invert::kYes)); 2193cb93a386Sopenharmony_ci rrectPathGeos.emplace_back(new RRectPathGeo( 2194cb93a386Sopenharmony_ci openRectPath, SkRect::MakeWH(10, 10), 2195cb93a386Sopenharmony_ci RRectPathGeo::RRectForStroke::kNo, PathGeo::Invert::kNo)); 2196cb93a386Sopenharmony_ci } 2197cb93a386Sopenharmony_ci 2198cb93a386Sopenharmony_ci { 2199cb93a386Sopenharmony_ci SkPath quadPath; 2200cb93a386Sopenharmony_ci quadPath.quadTo(10, 10, 5, 8); 2201cb93a386Sopenharmony_ci geos.emplace_back(new PathGeo(quadPath, PathGeo::Invert::kNo)); 2202cb93a386Sopenharmony_ci geos.emplace_back(new PathGeo(quadPath, PathGeo::Invert::kYes)); 2203cb93a386Sopenharmony_ci } 2204cb93a386Sopenharmony_ci 2205cb93a386Sopenharmony_ci { 2206cb93a386Sopenharmony_ci SkPath linePath; 2207cb93a386Sopenharmony_ci linePath.lineTo(10, 10); 2208cb93a386Sopenharmony_ci geos.emplace_back(new PathGeo(linePath, PathGeo::Invert::kNo)); 2209cb93a386Sopenharmony_ci geos.emplace_back(new PathGeo(linePath, PathGeo::Invert::kYes)); 2210cb93a386Sopenharmony_ci } 2211cb93a386Sopenharmony_ci 2212cb93a386Sopenharmony_ci // Horizontal and vertical paths become rrects when stroked. 2213cb93a386Sopenharmony_ci { 2214cb93a386Sopenharmony_ci SkPath vLinePath; 2215cb93a386Sopenharmony_ci vLinePath.lineTo(0, 10); 2216cb93a386Sopenharmony_ci geos.emplace_back(new PathGeo(vLinePath, PathGeo::Invert::kNo)); 2217cb93a386Sopenharmony_ci geos.emplace_back(new PathGeo(vLinePath, PathGeo::Invert::kYes)); 2218cb93a386Sopenharmony_ci } 2219cb93a386Sopenharmony_ci 2220cb93a386Sopenharmony_ci { 2221cb93a386Sopenharmony_ci SkPath hLinePath; 2222cb93a386Sopenharmony_ci hLinePath.lineTo(10, 0); 2223cb93a386Sopenharmony_ci geos.emplace_back(new PathGeo(hLinePath, PathGeo::Invert::kNo)); 2224cb93a386Sopenharmony_ci geos.emplace_back(new PathGeo(hLinePath, PathGeo::Invert::kYes)); 2225cb93a386Sopenharmony_ci } 2226cb93a386Sopenharmony_ci 2227cb93a386Sopenharmony_ci for (int i = 0; i < geos.count(); ++i) { 2228cb93a386Sopenharmony_ci test_basic(reporter, *geos[i]); 2229cb93a386Sopenharmony_ci test_scale(reporter, *geos[i]); 2230cb93a386Sopenharmony_ci test_dash_fill(reporter, *geos[i]); 2231cb93a386Sopenharmony_ci test_null_dash(reporter, *geos[i]); 2232cb93a386Sopenharmony_ci // Test modifying various stroke params. 2233cb93a386Sopenharmony_ci test_stroke_param<SkScalar>( 2234cb93a386Sopenharmony_ci reporter, *geos[i], 2235cb93a386Sopenharmony_ci [](SkPaint* p, SkScalar w) { p->setStrokeWidth(w);}, 2236cb93a386Sopenharmony_ci SkIntToScalar(2), SkIntToScalar(4)); 2237cb93a386Sopenharmony_ci test_stroke_join(reporter, *geos[i]); 2238cb93a386Sopenharmony_ci test_stroke_cap(reporter, *geos[i]); 2239cb93a386Sopenharmony_ci test_miter_limit(reporter, *geos[i]); 2240cb93a386Sopenharmony_ci test_path_effect_makes_rrect(reporter, *geos[i]); 2241cb93a386Sopenharmony_ci test_unknown_path_effect(reporter, *geos[i]); 2242cb93a386Sopenharmony_ci test_path_effect_makes_empty_shape(reporter, *geos[i]); 2243cb93a386Sopenharmony_ci test_path_effect_fails(reporter, *geos[i]); 2244cb93a386Sopenharmony_ci test_make_hairline_path_effect(reporter, *geos[i]); 2245cb93a386Sopenharmony_ci test_volatile_path(reporter, *geos[i]); 2246cb93a386Sopenharmony_ci } 2247cb93a386Sopenharmony_ci 2248cb93a386Sopenharmony_ci for (int i = 0; i < rrectPathGeos.count(); ++i) { 2249cb93a386Sopenharmony_ci const RRectPathGeo& rrgeo = *rrectPathGeos[i]; 2250cb93a386Sopenharmony_ci SkPaint fillPaint; 2251cb93a386Sopenharmony_ci TestCase fillPathCase(reporter, rrgeo.path(), fillPaint); 2252cb93a386Sopenharmony_ci SkRRect rrect; 2253cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, rrgeo.isNonPath(fillPaint) == 2254cb93a386Sopenharmony_ci fillPathCase.baseShape().asRRect(&rrect, nullptr, nullptr, 2255cb93a386Sopenharmony_ci nullptr)); 2256cb93a386Sopenharmony_ci if (rrgeo.isNonPath(fillPaint)) { 2257cb93a386Sopenharmony_ci TestCase fillPathCase2(reporter, rrgeo.path(), fillPaint); 2258cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, rrect == rrgeo.rrect()); 2259cb93a386Sopenharmony_ci TestCase fillRRectCase(reporter, rrect, fillPaint); 2260cb93a386Sopenharmony_ci fillPathCase2.compare(reporter, fillRRectCase, 2261cb93a386Sopenharmony_ci TestCase::kAllSame_ComparisonExpecation); 2262cb93a386Sopenharmony_ci } 2263cb93a386Sopenharmony_ci SkPaint strokePaint; 2264cb93a386Sopenharmony_ci strokePaint.setStrokeWidth(3.f); 2265cb93a386Sopenharmony_ci strokePaint.setStyle(SkPaint::kStroke_Style); 2266cb93a386Sopenharmony_ci TestCase strokePathCase(reporter, rrgeo.path(), strokePaint); 2267cb93a386Sopenharmony_ci if (rrgeo.isNonPath(strokePaint)) { 2268cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, strokePathCase.baseShape().asRRect(&rrect, nullptr, nullptr, 2269cb93a386Sopenharmony_ci nullptr)); 2270cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, rrect == rrgeo.rrect()); 2271cb93a386Sopenharmony_ci TestCase strokeRRectCase(reporter, rrect, strokePaint); 2272cb93a386Sopenharmony_ci strokePathCase.compare(reporter, strokeRRectCase, 2273cb93a386Sopenharmony_ci TestCase::kAllSame_ComparisonExpecation); 2274cb93a386Sopenharmony_ci } 2275cb93a386Sopenharmony_ci } 2276cb93a386Sopenharmony_ci 2277cb93a386Sopenharmony_ci // Test a volatile empty path. 2278cb93a386Sopenharmony_ci test_volatile_path(reporter, PathGeo(SkPath(), PathGeo::Invert::kNo)); 2279cb93a386Sopenharmony_ci} 2280cb93a386Sopenharmony_ci 2281cb93a386Sopenharmony_ciDEF_TEST(GrStyledShape_arcs, reporter) { 2282cb93a386Sopenharmony_ci SkStrokeRec roundStroke(SkStrokeRec::kFill_InitStyle); 2283cb93a386Sopenharmony_ci roundStroke.setStrokeStyle(2.f); 2284cb93a386Sopenharmony_ci roundStroke.setStrokeParams(SkPaint::kRound_Cap, SkPaint::kRound_Join, 1.f); 2285cb93a386Sopenharmony_ci 2286cb93a386Sopenharmony_ci SkStrokeRec squareStroke(roundStroke); 2287cb93a386Sopenharmony_ci squareStroke.setStrokeParams(SkPaint::kSquare_Cap, SkPaint::kRound_Join, 1.f); 2288cb93a386Sopenharmony_ci 2289cb93a386Sopenharmony_ci SkStrokeRec roundStrokeAndFill(roundStroke); 2290cb93a386Sopenharmony_ci roundStrokeAndFill.setStrokeStyle(2.f, true); 2291cb93a386Sopenharmony_ci 2292cb93a386Sopenharmony_ci static constexpr SkScalar kIntervals[] = {1, 2}; 2293cb93a386Sopenharmony_ci auto dash = SkDashPathEffect::Make(kIntervals, SK_ARRAY_COUNT(kIntervals), 1.5f); 2294cb93a386Sopenharmony_ci 2295cb93a386Sopenharmony_ci SkTArray<GrStyle> styles; 2296cb93a386Sopenharmony_ci styles.push_back(GrStyle::SimpleFill()); 2297cb93a386Sopenharmony_ci styles.push_back(GrStyle::SimpleHairline()); 2298cb93a386Sopenharmony_ci styles.push_back(GrStyle(roundStroke, nullptr)); 2299cb93a386Sopenharmony_ci styles.push_back(GrStyle(squareStroke, nullptr)); 2300cb93a386Sopenharmony_ci styles.push_back(GrStyle(roundStrokeAndFill, nullptr)); 2301cb93a386Sopenharmony_ci styles.push_back(GrStyle(roundStroke, dash)); 2302cb93a386Sopenharmony_ci 2303cb93a386Sopenharmony_ci for (const auto& style : styles) { 2304cb93a386Sopenharmony_ci // An empty rect never draws anything according to SkCanvas::drawArc() docs. 2305cb93a386Sopenharmony_ci TestCase emptyArc(GrStyledShape::MakeArc(SkRect::MakeEmpty(), 0, 90.f, false, style), 2306cb93a386Sopenharmony_ci reporter); 2307cb93a386Sopenharmony_ci TestCase emptyPath(reporter, SkPath(), style); 2308cb93a386Sopenharmony_ci emptyArc.compare(reporter, emptyPath, TestCase::kAllSame_ComparisonExpecation); 2309cb93a386Sopenharmony_ci 2310cb93a386Sopenharmony_ci static constexpr SkRect kOval1{0, 0, 50, 50}; 2311cb93a386Sopenharmony_ci static constexpr SkRect kOval2{50, 0, 100, 50}; 2312cb93a386Sopenharmony_ci // Test that swapping starting and ending angle doesn't change the shape unless the arc 2313cb93a386Sopenharmony_ci // has a path effect. Also test that different ovals produce different shapes. 2314cb93a386Sopenharmony_ci TestCase arc1CW(GrStyledShape::MakeArc(kOval1, 0, 90.f, false, style), reporter); 2315cb93a386Sopenharmony_ci TestCase arc1CCW(GrStyledShape::MakeArc(kOval1, 90.f, -90.f, false, style), reporter); 2316cb93a386Sopenharmony_ci 2317cb93a386Sopenharmony_ci TestCase arc1CWWithCenter(GrStyledShape::MakeArc(kOval1, 0, 90.f, true, style), reporter); 2318cb93a386Sopenharmony_ci TestCase arc1CCWWithCenter(GrStyledShape::MakeArc(kOval1, 90.f, -90.f, true, style), 2319cb93a386Sopenharmony_ci reporter); 2320cb93a386Sopenharmony_ci 2321cb93a386Sopenharmony_ci TestCase arc2CW(GrStyledShape::MakeArc(kOval2, 0, 90.f, false, style), reporter); 2322cb93a386Sopenharmony_ci TestCase arc2CWWithCenter(GrStyledShape::MakeArc(kOval2, 0, 90.f, true, style), reporter); 2323cb93a386Sopenharmony_ci 2324cb93a386Sopenharmony_ci auto reversedExepectations = style.hasPathEffect() 2325cb93a386Sopenharmony_ci ? TestCase::kAllDifferent_ComparisonExpecation 2326cb93a386Sopenharmony_ci : TestCase::kAllSame_ComparisonExpecation; 2327cb93a386Sopenharmony_ci arc1CW.compare(reporter, arc1CCW, reversedExepectations); 2328cb93a386Sopenharmony_ci arc1CWWithCenter.compare(reporter, arc1CCWWithCenter, reversedExepectations); 2329cb93a386Sopenharmony_ci arc1CW.compare(reporter, arc2CW, TestCase::kAllDifferent_ComparisonExpecation); 2330cb93a386Sopenharmony_ci arc1CW.compare(reporter, arc1CWWithCenter, TestCase::kAllDifferent_ComparisonExpecation); 2331cb93a386Sopenharmony_ci arc1CWWithCenter.compare(reporter, arc2CWWithCenter, 2332cb93a386Sopenharmony_ci TestCase::kAllDifferent_ComparisonExpecation); 2333cb93a386Sopenharmony_ci 2334cb93a386Sopenharmony_ci // Test that two arcs that start at the same angle but specified differently are equivalent. 2335cb93a386Sopenharmony_ci TestCase arc3A(GrStyledShape::MakeArc(kOval1, 224.f, 73.f, false, style), reporter); 2336cb93a386Sopenharmony_ci TestCase arc3B(GrStyledShape::MakeArc(kOval1, 224.f - 360.f, 73.f, false, style), reporter); 2337cb93a386Sopenharmony_ci arc3A.compare(reporter, arc3B, TestCase::kAllDifferent_ComparisonExpecation); 2338cb93a386Sopenharmony_ci 2339cb93a386Sopenharmony_ci // Test that an arc that traverses the entire oval (and then some) is equivalent to the 2340cb93a386Sopenharmony_ci // oval itself unless there is a path effect. 2341cb93a386Sopenharmony_ci TestCase ovalArc(GrStyledShape::MakeArc(kOval1, 150.f, -790.f, false, style), reporter); 2342cb93a386Sopenharmony_ci TestCase oval(GrStyledShape(SkRRect::MakeOval(kOval1)), reporter); 2343cb93a386Sopenharmony_ci auto ovalExpectations = style.hasPathEffect() ? TestCase::kAllDifferent_ComparisonExpecation 2344cb93a386Sopenharmony_ci : TestCase::kAllSame_ComparisonExpecation; 2345cb93a386Sopenharmony_ci if (style.strokeRec().getWidth() >= 0 && style.strokeRec().getCap() != SkPaint::kButt_Cap) { 2346cb93a386Sopenharmony_ci ovalExpectations = TestCase::kAllDifferent_ComparisonExpecation; 2347cb93a386Sopenharmony_ci } 2348cb93a386Sopenharmony_ci ovalArc.compare(reporter, oval, ovalExpectations); 2349cb93a386Sopenharmony_ci 2350cb93a386Sopenharmony_ci // If the the arc starts/ends at the center then it is then equivalent to the oval only for 2351cb93a386Sopenharmony_ci // simple fills. 2352cb93a386Sopenharmony_ci TestCase ovalArcWithCenter(GrStyledShape::MakeArc(kOval1, 304.f, 1225.f, true, style), 2353cb93a386Sopenharmony_ci reporter); 2354cb93a386Sopenharmony_ci ovalExpectations = style.isSimpleFill() ? TestCase::kAllSame_ComparisonExpecation 2355cb93a386Sopenharmony_ci : TestCase::kAllDifferent_ComparisonExpecation; 2356cb93a386Sopenharmony_ci ovalArcWithCenter.compare(reporter, oval, ovalExpectations); 2357cb93a386Sopenharmony_ci } 2358cb93a386Sopenharmony_ci} 2359cb93a386Sopenharmony_ci 2360cb93a386Sopenharmony_ciDEF_TEST(GrShapeInversion, r) { 2361cb93a386Sopenharmony_ci SkPath path; 2362cb93a386Sopenharmony_ci SkScalar radii[] = {10.f, 10.f, 10.f, 10.f, 2363cb93a386Sopenharmony_ci 10.f, 10.f, 10.f, 10.f}; 2364cb93a386Sopenharmony_ci path.addRoundRect(SkRect::MakeWH(50, 50), radii); 2365cb93a386Sopenharmony_ci path.toggleInverseFillType(); 2366cb93a386Sopenharmony_ci 2367cb93a386Sopenharmony_ci GrShape inverseRRect(path); 2368cb93a386Sopenharmony_ci GrShape rrect(inverseRRect); 2369cb93a386Sopenharmony_ci rrect.setInverted(false); 2370cb93a386Sopenharmony_ci 2371cb93a386Sopenharmony_ci REPORTER_ASSERT(r, inverseRRect.inverted() && inverseRRect.isPath()); 2372cb93a386Sopenharmony_ci REPORTER_ASSERT(r, !rrect.inverted() && rrect.isPath()); 2373cb93a386Sopenharmony_ci 2374cb93a386Sopenharmony_ci // Invertedness should be preserved after simplification 2375cb93a386Sopenharmony_ci inverseRRect.simplify(); 2376cb93a386Sopenharmony_ci rrect.simplify(); 2377cb93a386Sopenharmony_ci 2378cb93a386Sopenharmony_ci REPORTER_ASSERT(r, inverseRRect.inverted() && inverseRRect.isRRect()); 2379cb93a386Sopenharmony_ci REPORTER_ASSERT(r, !rrect.inverted() && rrect.isRRect()); 2380cb93a386Sopenharmony_ci 2381cb93a386Sopenharmony_ci // Invertedness should be reset when calling reset(). 2382cb93a386Sopenharmony_ci inverseRRect.reset(); 2383cb93a386Sopenharmony_ci REPORTER_ASSERT(r, !inverseRRect.inverted() && inverseRRect.isEmpty()); 2384cb93a386Sopenharmony_ci inverseRRect.setPath(path); 2385cb93a386Sopenharmony_ci inverseRRect.reset(); 2386cb93a386Sopenharmony_ci REPORTER_ASSERT(r, !inverseRRect.inverted() && inverseRRect.isEmpty()); 2387cb93a386Sopenharmony_ci} 2388