| /* | |
| * 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 "gm/gm.h" | |
| #include "include/core/SkCanvas.h" | |
| #include "include/core/SkPaint.h" | |
| #include "include/core/SkPath.h" | |
| #include "include/core/SkPathBuilder.h" | |
| #include "include/core/SkRRect.h" | |
| #include "include/core/SkRect.h" | |
| // Tests clipRRect cases that Graphite should handle with an analytic shader. | |
| DEF_SIMPLE_GM(clip_rrects_analytic, canvas, 360, 325) { | |
| const SkRect baseRect = {0, 0, 32, 32}; | |
| auto makeRRect = [&](bool tl, bool tr, bool br, bool bl) { | |
| const float cornerRadius = 8; | |
| const SkVector radii[4] = { | |
| {tl ? cornerRadius : 0.f, tl ? cornerRadius : 0.f}, | |
| {tr ? cornerRadius : 0.f, tr ? cornerRadius : 0.f}, | |
| {br ? cornerRadius : 0.f, br ? cornerRadius : 0.f}, | |
| {bl ? cornerRadius : 0.f, bl ? cornerRadius : 0.f} | |
| }; | |
| return SkRRect::MakeRectRadii(baseRect, radii); | |
| }; | |
| // Test all combinations of circular corners | |
| SkRRect rrects[] = { | |
| makeRRect(false, false, false, false), | |
| makeRRect(true, false, false, false), | |
| makeRRect(false, true, false, false), | |
| makeRRect(false, false, true, false), | |
| makeRRect(false, false, false, true), | |
| makeRRect(true, true, false, false), | |
| makeRRect(true, false, true, false), | |
| makeRRect(true, false, false, true), | |
| makeRRect(false, true, true, false), | |
| makeRRect(false, true, false, true), | |
| makeRRect(false, false, true, true), | |
| makeRRect(true, true, true, false), | |
| makeRRect(true, true, false, true), | |
| makeRRect(true, false, true, true), | |
| makeRRect(false, true, true, true), | |
| makeRRect(true, true, true, true) | |
| }; | |
| std::pair<SkPoint, SkMatrix> transforms[] = { | |
| // Identity (top-left grid) | |
| {{0.f, 0.f}, SkMatrix()}, | |
| // Non-uniform scale (top-right grid) | |
| {{160.f, 0.f}, SkMatrix::Scale(1.2f, 0.8f)}, | |
| // Orthgonal (bot-left grid) | |
| {{20.f, 160.f}, SkMatrix::RotateDeg(10.f, {2 * baseRect.width(), 2 * baseRect.height()})}, | |
| // Fully affine (bot-right grid) | |
| {{180.f, 140.f}, SkMatrix::Skew(0.2f, 0.1f)} | |
| }; | |
| // Make this a complicated enough shape that Graphite doesn't just apply the clip geometrically. | |
| SkPathBuilder b; | |
| b.addRect(baseRect); | |
| b.addRect(baseRect.makeInset(10, 10)); | |
| b.setFillType(SkPathFillType::kEvenOdd); | |
| SkPath p = b.detach(); | |
| SkPaint fillPaint; | |
| fillPaint.setColor(SK_ColorBLACK); | |
| fillPaint.setAntiAlias(true); | |
| for (auto [origin, xform] : transforms) { | |
| canvas->save(); | |
| canvas->translate(origin.fX, origin.fY); | |
| int x = 0; | |
| int y = 0; | |
| for (const SkRRect& clip : rrects) { | |
| canvas->save(); | |
| canvas->concat(xform); | |
| canvas->translate(5.f * (x + 1) + baseRect.width() * x, | |
| 5.f * (y + 1) + baseRect.height() * y); | |
| canvas->clipRRect(clip, true); | |
| canvas->drawPath(p, fillPaint); | |
| canvas->restore(); | |
| x++; | |
| if (x >= 4) { | |
| x = 0; | |
| y++; | |
| } | |
| } | |
| canvas->restore(); | |
| } | |
| } |