diff --git a/src/gpu/GrClipStack.cpp b/src/gpu/GrClipStack.cpp index 0072727451..5eb97dc2df 100644 --- a/src/gpu/GrClipStack.cpp +++ b/src/gpu/GrClipStack.cpp @@ -125,7 +125,7 @@ static bool shape_contains_rect( if (!mixedAAMode && aToDevice == bToDevice) { // A and B are in the same coordinate space, so don't bother mapping return a.conservativeContains(b); - } else if (bToDevice.isIdentity() && aToDevice.isScaleTranslate()) { + } else if (bToDevice.isIdentity() && aToDevice.preservesAxisAlignment()) { // Optimize the common case of draws (B, with identity matrix) and axis-aligned shapes, // instead of checking the four corners separately. SkRect bInA = b; @@ -543,7 +543,7 @@ void GrClipStack::RawElement::simplify(const SkIRect& deviceBounds, bool forceAA // Except for axis-aligned clip rects, upgrade to AA when forced. We skip axis-aligned clip // rects because a non-AA axis aligned rect can always be set as just a scissor test or window // rect, avoiding an expensive stencil mask generation. - if (forceAA && !(fShape.isRect() && fLocalToDevice.isScaleTranslate())) { + if (forceAA && !(fShape.isRect() && fLocalToDevice.preservesAxisAlignment())) { fAA = GrAA::kYes; } @@ -551,7 +551,7 @@ void GrClipStack::RawElement::simplify(const SkIRect& deviceBounds, bool forceAA // mapped bounds of the shape. fOuterBounds = GrClip::GetPixelIBounds(outer, fAA, BoundsType::kExterior); - if (fLocalToDevice.isScaleTranslate()) { + if (fLocalToDevice.preservesAxisAlignment()) { if (fShape.isRect()) { // The actual geometry can be updated to the device-intersected bounds and we can // know the inner bounds diff --git a/tests/GrClipStackTest.cpp b/tests/GrClipStackTest.cpp index 372e8653c6..c81fcae7ee 100644 --- a/tests/GrClipStackTest.cpp +++ b/tests/GrClipStackTest.cpp @@ -1108,6 +1108,7 @@ DEF_TEST(GrClipStack_DeviceRRect, r) { DEF_TEST(GrClipStack_ScaleTranslate, r) { SkMatrix lm = SkMatrix::Scale(2.f, 4.f); lm.postTranslate(15.5f, 14.3f); + SkASSERT(lm.preservesAxisAlignment() && lm.isScaleTranslate()); // Rect -> matrix is applied up front SkRect rect = {0.f, 0.f, 10.f, 10.f}; @@ -1140,6 +1141,43 @@ DEF_TEST(GrClipStack_ScaleTranslate, r) { .finishTest()); } +// Tests that rect-stays-rect matrices that are not scale+translate matrices are pre-applied. +DEF_TEST(GrClipStack_PreserveAxisAlignment, r) { + SkMatrix lm = SkMatrix::RotateDeg(90.f); + lm.postTranslate(15.5f, 14.3f); + SkASSERT(lm.preservesAxisAlignment() && !lm.isScaleTranslate()); + + // Rect -> matrix is applied up front + SkRect rect = {0.f, 0.f, 10.f, 10.f}; + run_test_case(r, TestCase::Build("r90+rect", kDeviceBounds) + .actual().rect(rect, lm, GrAA::kYes, SkClipOp::kIntersect) + .finishElements() + .expect().rect(lm.mapRect(rect), GrAA::kYes, SkClipOp::kIntersect) + .finishElements() + .state(GrClipStack::ClipState::kDeviceRect) + .finishTest()); + + // RRect -> matrix is applied up front + SkRRect localRRect = SkRRect::MakeRectXY(rect, 2.f, 2.f); + SkRRect deviceRRect; + SkAssertResult(localRRect.transform(lm, &deviceRRect)); + run_test_case(r, TestCase::Build("r90+rrect", kDeviceBounds) + .actual().rrect(localRRect, lm, GrAA::kYes, SkClipOp::kIntersect) + .finishElements() + .expect().rrect(deviceRRect, GrAA::kYes, SkClipOp::kIntersect) + .finishElements() + .state(GrClipStack::ClipState::kDeviceRRect) + .finishTest()); + + // Path -> matrix is NOT applied + run_test_case(r, TestCase::Build("r90+path", kDeviceBounds) + .actual().intersect().localToDevice(lm).path(make_octagon(rect)) + .finishElements() + .expectActual() + .state(GrClipStack::ClipState::kComplex) + .finishTest()); +} + // Tests that a convex path element can contain a rect or round rect, allowing the stack to be // simplified DEF_TEST(GrClipStack_ConvexPathContains, r) {