| /* | |
| * Copyright 2026 Google LLC | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "include/core/SkPathBuilder.h" | |
| #include "include/gpu/ganesh/GrDirectContext.h" | |
| #include "src/gpu/ganesh/GrStyle.h" | |
| #include "src/gpu/ganesh/SurfaceDrawContext.h" | |
| #include "tests/Test.h" | |
| #include "tools/gpu/ContextType.h" | |
| static void only_allow_small(GrContextOptions* options) { | |
| options->fGpuPathRenderers = GpuPathRenderers::kSmall; | |
| } | |
| DEF_GANESH_TEST_FOR_CONTEXTS(SmallPathRenderer_crbug505876830, | |
| skgpu::IsRenderingContext, | |
| reporter, | |
| ctxInfo, | |
| only_allow_small, | |
| CtsEnforcement::kNever) { | |
| auto ctx = ctxInfo.directContext(); | |
| auto sdc = skgpu::ganesh::SurfaceDrawContext::Make( | |
| ctx, GrColorType::kRGBA_8888, nullptr, SkBackingFit::kExact, | |
| {100, 100}, SkSurfaceProps(), /*label=*/{}); | |
| if (!sdc) { | |
| return; | |
| } | |
| sdc->clear(SK_PMColor4fBLACK); | |
| GrStyle style(SkStrokeRec::kFill_InitStyle); | |
| const SkPath smolpath = SkPathBuilder() | |
| .moveTo(-0.25000017881393432617f, -0.25000017881393432617f) | |
| .lineTo( 0.25000017881393432617f, -0.25000017881393432617f) | |
| .lineTo( 0.25000017881393432617f, 0.25000017881393432617f) | |
| .close() | |
| .detach(); | |
| const SkMatrix m = SkMatrix::MakeAll( | |
| -0.0f, 70368341861093281422320336896.0f, -590295810358705651712.0f, | |
| 2.0f, 17469300226849243136.0f, 0.0f, | |
| 868052350533632.0f, 4220791729285326409039872.0f, 3572.759765625f | |
| ); | |
| // Passes if we don't assert. | |
| sdc->drawPath(nullptr, GrPaint(), GrAA::kYes, m, smolpath, style); | |
| ctx->flushAndSubmit(); | |
| } | |
| #if !defined(SK_ENABLE_OPTIMIZE_SIZE) | |
| #include "src/core/SkFloatBits.h" | |
| #include "src/gpu/ganesh/geometry/GrStyledShape.h" | |
| #include "src/gpu/ganesh/ops/SmallPathShapeData.h" | |
| DEF_TEST(SmallPathShapeDataKey_DomainSeparation, reporter) { | |
| SkPath path = SkPath::Polygon({{-1, -1}, {1, -1}, {1, 1}, {-1, 1}}, true); | |
| GrStyledShape shape(path); | |
| skgpu::ganesh::SmallPathShapeDataKey sdfKey(shape, 32); | |
| skgpu::ganesh::SmallPathShapeDataKey bmKey(shape, SkMatrix::I()); | |
| REPORTER_ASSERT(reporter, !(sdfKey == bmKey)); | |
| REPORTER_ASSERT(reporter, sdfKey.data()[0] == | |
| static_cast<uint32_t>(skgpu::ganesh::SmallPathShapeDataKey::Type::kSDF)); | |
| REPORTER_ASSERT(reporter, bmKey.data()[0] == | |
| static_cast<uint32_t>(skgpu::ganesh::SmallPathShapeDataKey::Type::kBitmap)); | |
| } | |
| DEF_TEST(SmallPathShapeDataKey_CollisionReproduction, reporter) { | |
| // 1. Construct Shape B (Bitmap side) | |
| const float p0_x = SkBits2Float(0x28); | |
| const float p0_y = 0.45f; | |
| const float p1_x = 0.2f; // "free" | |
| const float p1_y = 0.45f; | |
| const float p2_x = 0.49f; | |
| const float p2_y = SkBits2Float(0x700000); | |
| const float p3_x = 65.0f; | |
| const float p3_y = SkBits2Float(0x6); | |
| const float p4_x = -1.0f; | |
| const float p4_y = 1.5f; | |
| SkPath pathB = SkPathBuilder() | |
| .moveTo(p0_x, p0_y) | |
| .lineTo(p1_x, p1_y) | |
| .lineTo(p2_x, p2_y) | |
| .quadTo(p3_x, p3_y, p4_x, p4_y) | |
| .detach(); | |
| pathB.setFillType(SkPathFillType::kWinding); | |
| SkMatrix ctmB = SkMatrix::MakeAll( | |
| SkBits2Float(162), -1.0f, 0.0f, | |
| -1.0f, SkBits2Float(0x10C), 0.0f, | |
| 0.0f, 0.0f, 1.0f | |
| ); | |
| GrStyledShape shapeB(pathB); | |
| skgpu::ganesh::SmallPathShapeDataKey keyB(shapeB, ctmB); | |
| // 2. Construct Shape A (DF side) | |
| SkRect rectA = SkRect::MakeLTRB(-1.0f, -1.0f, 0.0f, SkBits2Float(0x110)); | |
| SkVector radiiA[4] = { | |
| { SkBits2Float(4), SkBits2Float(0x02010100) }, // UL | |
| { SkBits2Float(0x28), 0.45f }, // UR | |
| { 0.2f, 0.45f }, // LR | |
| { 0.49f, SkBits2Float(0x700000) } // LL | |
| }; | |
| SkRRect rrectA; | |
| rrectA.setRectRadii(rectA, radiiA); | |
| SkPaint strokePaint; | |
| strokePaint.setStyle(SkPaint::kStroke_Style); | |
| strokePaint.setStrokeWidth(1.5f); | |
| strokePaint.setStrokeJoin(SkPaint::kRound_Join); | |
| strokePaint.setStrokeCap(SkPaint::kButt_Cap); | |
| GrStyle styleA(strokePaint); | |
| GrStyledShape parentShapeA(rrectA, styleA); | |
| GrStyledShape shapeA = parentShapeA.applyStyle(GrStyle::Apply::kPathEffectAndStrokeRec, 65.0f); | |
| skgpu::ganesh::SmallPathShapeDataKey keyA(shapeA, 162); | |
| // Both keys have identical lengths (19 words: 1 word tag + 18 payload words) | |
| REPORTER_ASSERT(reporter, keyA.count32() == 19); | |
| REPORTER_ASSERT(reporter, keyB.count32() == 19); | |
| // Verify the 18 payload words match byte-for-byte! | |
| // Without the domain tag, this would have been an exact collision. | |
| int payloadDiff = memcmp(&keyA.data()[1], &keyB.data()[1], 18 * sizeof(uint32_t)); | |
| REPORTER_ASSERT(reporter, payloadDiff == 0); | |
| // With the domain tag, keyA != keyB: | |
| REPORTER_ASSERT(reporter, !(keyA == keyB)); | |
| REPORTER_ASSERT(reporter, keyA.data()[0] == | |
| static_cast<uint32_t>(skgpu::ganesh::SmallPathShapeDataKey::Type::kSDF)); | |
| REPORTER_ASSERT(reporter, keyB.data()[0] == | |
| static_cast<uint32_t>(skgpu::ganesh::SmallPathShapeDataKey::Type::kBitmap)); | |
| } | |
| #endif // !defined(SK_ENABLE_OPTIMIZE_SIZE) | |