Encapsulate GrReducedClip result in class members

Updates GrReducedClip to store its result in class members instead of
various pointer arguments. This helps clean up calling code and will
make it easier to reduce the clip higher in the stack.

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2222873002

Review-Url: https://codereview.chromium.org/2222873002
diff --git a/src/gpu/GrClipStackClip.cpp b/src/gpu/GrClipStackClip.cpp
index 4bd6d55..e117144 100644
--- a/src/gpu/GrClipStackClip.cpp
+++ b/src/gpu/GrClipStackClip.cpp
@@ -18,6 +18,7 @@
 
 typedef SkClipStack::Element Element;
 typedef GrReducedClip::InitialState InitialState;
+typedef GrReducedClip::ElementList ElementList;
 
 static const int kMaxAnalyticElements = 4;
 
@@ -134,7 +135,7 @@
                                     bool hasUserStencilSettings,
                                     const GrDrawContext* drawContext,
                                     const SkVector& clipToMaskOffset,
-                                    const GrReducedClip::ElementList& elements) {
+                                    const ElementList& elements) {
     // TODO: generalize this function so that when
     // a clip gets complex enough it can just be done in SW regardless
     // of whether it would invoke the GrSoftwarePathRenderer.
@@ -143,7 +144,7 @@
     // space.
     const SkMatrix translate = SkMatrix::MakeTrans(clipToMaskOffset.fX, clipToMaskOffset.fY);
 
-    for (GrReducedClip::ElementList::Iter iter(elements.headIter()); iter.get(); iter.next()) {
+    for (ElementList::Iter iter(elements); iter.get(); iter.next()) {
         const Element* element = iter.get();
 
         SkRegion::Op op = element->getOp();
@@ -159,7 +160,7 @@
     return false;
 }
 
-static bool get_analytic_clip_processor(const GrReducedClip::ElementList& elements,
+static bool get_analytic_clip_processor(const ElementList& elements,
                                         bool abortIfAA,
                                         const SkVector& clipToRTOffset,
                                         const SkRect& drawBounds,
@@ -168,7 +169,7 @@
     boundsInClipSpace = drawBounds.makeOffset(-clipToRTOffset.fX, -clipToRTOffset.fY);
     SkASSERT(elements.count() <= kMaxAnalyticElements);
     SkSTArray<kMaxAnalyticElements, sk_sp<GrFragmentProcessor>> fps;
-    GrReducedClip::ElementList::Iter iter(elements);
+    ElementList::Iter iter(elements);
     while (iter.get()) {
         SkRegion::Op op = iter.get()->getOp();
         bool invert;
@@ -259,28 +260,19 @@
     const SkScalar clipX = SkIntToScalar(fOrigin.x()),
                    clipY = SkIntToScalar(fOrigin.y());
 
-    GrReducedClip::ElementList elements;
-    int32_t genID = 0;
-    SkIRect clipSpaceIBounds;
-    bool requiresAA = false;
+    SkRect clipSpaceDevBounds = devBounds.makeOffset(clipX, clipY);
+    const GrReducedClip reducedClip(*fStack, clipSpaceDevBounds);
 
-    InitialState initialState = GrReducedClip::ReduceClipStack(*fStack,
-                                                               devBounds.makeOffset(clipX, clipY),
-                                                               &elements,
-                                                               &genID,
-                                                               &clipSpaceIBounds,
-                                                               &requiresAA);
-    if (elements.isEmpty()) {
-        if (GrReducedClip::kAllOut_InitialState == initialState || clipSpaceIBounds.isEmpty()) {
+    if (reducedClip.elements().isEmpty()) {
+        if (GrReducedClip::InitialState::kAllOut == reducedClip.initialState()) {
             return false;
-        } else {
-            SkIRect scissorSpaceIBounds(clipSpaceIBounds);
-            scissorSpaceIBounds.offset(-fOrigin);
-            if (!GrClip::IsInsideClip(scissorSpaceIBounds, devBounds)) {
-                out->makeScissored(scissorSpaceIBounds);
-            }
-            return true;
         }
+        if (!GrClip::IsInsideClip(reducedClip.iBounds(), clipSpaceDevBounds)) {
+            SkIRect scissorSpaceIBounds(reducedClip.iBounds());
+            scissorSpaceIBounds.offset(-fOrigin);
+            out->makeScissored(scissorSpaceIBounds);
+        }
+        return true;
     }
 
     // An element count of 4 was chosen because of the common pattern in Blink of:
@@ -291,7 +283,7 @@
     // when drawing rounded div borders. This could probably be tuned based on a
     // configuration's relative costs of switching RTs to generate a mask vs
     // longer shaders.
-    if (elements.count() <= kMaxAnalyticElements) {
+    if (reducedClip.elements().count() <= kMaxAnalyticElements) {
         // When there are multiple samples we want to do per-sample clipping, not compute a
         // fractional pixel coverage.
         bool disallowAnalyticAA = drawContext->isStencilBufferMultisampled();
@@ -302,10 +294,10 @@
             disallowAnalyticAA = useHWAA || hasUserStencilSettings;
         }
         sk_sp<GrFragmentProcessor> clipFP;
-        if (requiresAA &&
-            get_analytic_clip_processor(elements, disallowAnalyticAA, {-clipX, -clipY}, devBounds,
-                                        &clipFP)) {
-            SkIRect scissorSpaceIBounds(clipSpaceIBounds);
+        if (reducedClip.requiresAA() &&
+            get_analytic_clip_processor(reducedClip.elements(), disallowAnalyticAA,
+                                        {-clipX, -clipY}, devBounds, &clipFP)) {
+            SkIRect scissorSpaceIBounds(reducedClip.iBounds());
             scissorSpaceIBounds.offset(-fOrigin);
             if (GrClip::IsInsideClip(scissorSpaceIBounds, devBounds)) {
                 out->makeFPBased(std::move(clipFP), SkRect::Make(scissorSpaceIBounds));
@@ -317,32 +309,23 @@
     }
 
     // If the stencil buffer is multisampled we can use it to do everything.
-    if (!drawContext->isStencilBufferMultisampled() && requiresAA) {
+    if (!drawContext->isStencilBufferMultisampled() && reducedClip.requiresAA()) {
         sk_sp<GrTexture> result;
 
         // The top-left of the mask corresponds to the top-left corner of the bounds.
         SkVector clipToMaskOffset = {
-            SkIntToScalar(-clipSpaceIBounds.fLeft),
-            SkIntToScalar(-clipSpaceIBounds.fTop)
+            SkIntToScalar(-reducedClip.left()),
+            SkIntToScalar(-reducedClip.top())
         };
 
         if (UseSWOnlyPath(context, hasUserStencilSettings, drawContext,
-                          clipToMaskOffset, elements)) {
+                          clipToMaskOffset, reducedClip.elements())) {
             // The clip geometry is complex enough that it will be more efficient to create it
             // entirely in software
-            result = CreateSoftwareClipMask(context->textureProvider(),
-                                            genID,
-                                            initialState,
-                                            elements,
-                                            clipToMaskOffset,
-                                            clipSpaceIBounds);
+            result = CreateSoftwareClipMask(context->textureProvider(), reducedClip,
+                                            clipToMaskOffset);
         } else {
-            result = CreateAlphaClipMask(context,
-                                         genID,
-                                         initialState,
-                                         elements,
-                                         clipToMaskOffset,
-                                         clipSpaceIBounds);
+            result = CreateAlphaClipMask(context, reducedClip, clipToMaskOffset);
             // If createAlphaClipMask fails it means UseSWOnlyPath has a bug
             SkASSERT(result);
         }
@@ -350,7 +333,7 @@
         if (result) {
             // The mask's top left coord should be pinned to the rounded-out top left corner of
             // clipSpace bounds. We determine the mask's position WRT to the render target here.
-            SkIRect rtSpaceMaskBounds = clipSpaceIBounds;
+            SkIRect rtSpaceMaskBounds = reducedClip.iBounds();
             rtSpaceMaskBounds.offset(-fOrigin);
             out->makeFPBased(create_fp_for_mask(result.get(), rtSpaceMaskBounds),
                              SkRect::Make(rtSpaceMaskBounds));
@@ -361,22 +344,16 @@
 
     // use the stencil clip if we can't represent the clip as a rectangle.
     SkIPoint clipSpaceToStencilSpaceOffset = -fOrigin;
-    CreateStencilClipMask(context,
-                          drawContext,
-                          genID,
-                          initialState,
-                          elements,
-                          clipSpaceIBounds,
-                          clipSpaceToStencilSpaceOffset);
+    CreateStencilClipMask(context, drawContext, reducedClip, clipSpaceToStencilSpaceOffset);
 
     // This must occur after createStencilClipMask. That function may change the scissor. Also, it
     // only guarantees that the stencil mask is correct within the bounds it was passed, so we must
     // use both stencil and scissor test to the bounds for the final draw.
-    SkIRect scissorSpaceIBounds(clipSpaceIBounds);
-    scissorSpaceIBounds.offset(clipSpaceToStencilSpaceOffset);
-    if (GrClip::IsInsideClip(scissorSpaceIBounds, devBounds)) {
+    if (GrClip::IsInsideClip(reducedClip.iBounds(), clipSpaceDevBounds)) {
         out->makeStencil(true, devBounds);
     } else {
+        SkIRect scissorSpaceIBounds(reducedClip.iBounds());
+        scissorSpaceIBounds.offset(clipSpaceToStencilSpaceOffset);
         out->makeScissoredStencil(scissorSpaceIBounds);
     }
     return true;
@@ -457,14 +434,11 @@
 }
 
 sk_sp<GrTexture> GrClipStackClip::CreateAlphaClipMask(GrContext* context,
-                                                      int32_t elementsGenID,
-                                                      GrReducedClip::InitialState initialState,
-                                                      const GrReducedClip::ElementList& elements,
-                                                      const SkVector& clipToMaskOffset,
-                                                      const SkIRect& clipSpaceIBounds) {
+                                                      const GrReducedClip& reducedClip,
+                                                      const SkVector& clipToMaskOffset) {
     GrResourceProvider* resourceProvider = context->resourceProvider();
     GrUniqueKey key;
-    GetClipMaskKey(elementsGenID, clipSpaceIBounds, &key);
+    GetClipMaskKey(reducedClip.genID(), reducedClip.iBounds(), &key);
     if (GrTexture* texture = resourceProvider->findAndRefTextureByUniqueKey(key)) {
         return sk_sp<GrTexture>(texture);
     }
@@ -476,8 +450,8 @@
     }
 
     sk_sp<GrDrawContext> dc(context->makeDrawContext(SkBackingFit::kApprox,
-                                                     clipSpaceIBounds.width(),
-                                                     clipSpaceIBounds.height(),
+                                                     reducedClip.width(),
+                                                     reducedClip.height(),
                                                      config, nullptr));
     if (!dc) {
         return nullptr;
@@ -485,12 +459,12 @@
     
     // The texture may be larger than necessary, this rect represents the part of the texture
     // we populate with a rasterization of the clip.
-    SkIRect maskSpaceIBounds = SkIRect::MakeWH(clipSpaceIBounds.width(), clipSpaceIBounds.height());
+    SkIRect maskSpaceIBounds = SkIRect::MakeWH(reducedClip.width(), reducedClip.height());
 
     // The scratch texture that we are drawing into can be substantially larger than the mask. Only
     // clear the part that we care about.
     dc->clear(&maskSpaceIBounds,
-              GrReducedClip::kAllIn_InitialState == initialState ? 0xffffffff : 0x00000000,
+              GrReducedClip::InitialState::kAllIn == reducedClip.initialState() ? -1 : 0,
               true);
 
     // Set the matrix so that rendered clip elements are transformed to mask space from clip
@@ -503,7 +477,7 @@
     // cleared.
 
     // walk through each clip element and perform its set op
-    for (GrReducedClip::ElementList::Iter iter = elements.headIter(); iter.get(); iter.next()) {
+    for (ElementList::Iter iter(reducedClip.elements()); iter.get(); iter.next()) {
         const Element* element = iter.get();
         SkRegion::Op op = element->getOp();
         bool invert = element->isInverseFilled();
@@ -539,7 +513,7 @@
             if (!dc->drawContextPriv().drawAndStencilRect(clip, &kDrawOutsideElement,
                                                           op, !invert, false,
                                                           translate,
-                                                          SkRect::Make(clipSpaceIBounds))) {
+                                                          SkRect::Make(reducedClip.iBounds()))) {
                 return nullptr;
             }
         } else {
@@ -563,10 +537,7 @@
 // (as opposed to canvas) coordinates
 bool GrClipStackClip::CreateStencilClipMask(GrContext* context,
                                             GrDrawContext* drawContext,
-                                            int32_t elementsGenID,
-                                            GrReducedClip::InitialState initialState,
-                                            const GrReducedClip::ElementList& elements,
-                                            const SkIRect& clipSpaceIBounds,
+                                            const GrReducedClip& reducedClip,
                                             const SkIPoint& clipSpaceToStencilOffset) {
     SkASSERT(drawContext);
 
@@ -577,8 +548,10 @@
     }
 
     // TODO: these need to be swapped over to using a StencilAttachmentProxy
-    if (stencilAttachment->mustRenderClip(elementsGenID, clipSpaceIBounds, clipSpaceToStencilOffset)) {
-        stencilAttachment->setLastClip(elementsGenID, clipSpaceIBounds, clipSpaceToStencilOffset);
+    if (stencilAttachment->mustRenderClip(reducedClip.genID(), reducedClip.iBounds(),
+                                          clipSpaceToStencilOffset)) {
+        stencilAttachment->setLastClip(reducedClip.genID(), reducedClip.iBounds(),
+                                       clipSpaceToStencilOffset);
         // Set the matrix so that rendered clip elements are transformed from clip to stencil space.
         SkVector translate = {
             SkIntToScalar(clipSpaceToStencilOffset.fX),
@@ -588,17 +561,16 @@
         viewMatrix.setTranslate(translate);
 
         // We set the current clip to the bounds so that our recursive draws are scissored to them.
-        SkIRect stencilSpaceIBounds(clipSpaceIBounds);
+        SkIRect stencilSpaceIBounds(reducedClip.iBounds());
         stencilSpaceIBounds.offset(clipSpaceToStencilOffset);
         GrFixedClip clip(stencilSpaceIBounds);
 
-        drawContext->drawContextPriv().clearStencilClip(
-                                            stencilSpaceIBounds,
-                                            GrReducedClip::kAllIn_InitialState == initialState);
+        bool insideClip = GrReducedClip::InitialState::kAllIn == reducedClip.initialState();
+        drawContext->drawContextPriv().clearStencilClip(stencilSpaceIBounds, insideClip);
 
         // walk through each clip element and perform its set op
         // with the existing clip.
-        for (GrReducedClip::ElementList::Iter iter(elements.headIter()); iter.get(); iter.next()) {
+        for (ElementList::Iter iter(reducedClip.elements()); iter.get(); iter.next()) {
             const Element* element = iter.get();
             bool useHWAA = element->isAA() && drawContext->isStencilBufferMultisampled();
 
@@ -732,11 +704,11 @@
                 } else {
                     // The view matrix is setup to do clip space -> stencil space translation, so
                     // draw rect in clip space.
-                    SkRect bounds = SkRect::Make(clipSpaceIBounds);
+                    SkRect bounds = SkRect::Make(reducedClip.iBounds());
                     bounds.offset(translate.fX, translate.fY);
                     clip.enableStencilClip(bounds);
                     drawContext->drawContextPriv().stencilRect(clip, *pass, false, viewMatrix,
-                                                               SkRect::Make(clipSpaceIBounds));
+                                                               SkRect::Make(reducedClip.iBounds()));
                 }
             }
         }
@@ -746,20 +718,17 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 sk_sp<GrTexture> GrClipStackClip::CreateSoftwareClipMask(GrTextureProvider* texProvider,
-                                                         int32_t elementsGenID,
-                                                         GrReducedClip::InitialState initialState,
-                                                         const GrReducedClip::ElementList& elements,
-                                                         const SkVector& clipToMaskOffset,
-                                                         const SkIRect& clipSpaceIBounds) {
+                                                         const GrReducedClip& reducedClip,
+                                                         const SkVector& clipToMaskOffset) {
     GrUniqueKey key;
-    GetClipMaskKey(elementsGenID, clipSpaceIBounds, &key);
+    GetClipMaskKey(reducedClip.genID(), reducedClip.iBounds(), &key);
     if (GrTexture* texture = texProvider->findAndRefTextureByUniqueKey(key)) {
         return sk_sp<GrTexture>(texture);
     }
 
     // The mask texture may be larger than necessary. We round out the clip space bounds and pin
     // the top left corner of the resulting rect to the top left of the texture.
-    SkIRect maskSpaceIBounds = SkIRect::MakeWH(clipSpaceIBounds.width(), clipSpaceIBounds.height());
+    SkIRect maskSpaceIBounds = SkIRect::MakeWH(reducedClip.width(), reducedClip.height());
 
     GrSWMaskHelper helper(texProvider);
 
@@ -769,9 +738,9 @@
     translate.setTranslate(clipToMaskOffset);
 
     helper.init(maskSpaceIBounds, &translate);
-    helper.clear(GrReducedClip::kAllIn_InitialState == initialState ? 0xFF : 0x00);
+    helper.clear(GrReducedClip::InitialState::kAllIn == reducedClip.initialState() ? 0xFF : 0x00);
 
-    for (GrReducedClip::ElementList::Iter iter(elements.headIter()) ; iter.get(); iter.next()) {
+    for (ElementList::Iter iter(reducedClip.elements()); iter.get(); iter.next()) {
         const Element* element = iter.get();
         SkRegion::Op op = element->getOp();
 
@@ -781,7 +750,7 @@
             // but leave the pixels inside the geometry alone. For reverse difference we invert all
             // the pixels before clearing the ones outside the geometry.
             if (SkRegion::kReverseDifference_Op == op) {
-                SkRect temp = SkRect::Make(clipSpaceIBounds);
+                SkRect temp = SkRect::Make(reducedClip.iBounds());
                 // invert the entire scene
                 helper.drawRect(temp, SkRegion::kXOR_Op, false, 0xFF);
             }
@@ -807,8 +776,8 @@
 
     // Allocate clip mask texture
     GrSurfaceDesc desc;
-    desc.fWidth = clipSpaceIBounds.width();
-    desc.fHeight = clipSpaceIBounds.height();
+    desc.fWidth = reducedClip.width();
+    desc.fHeight = reducedClip.height();
     desc.fConfig = kAlpha_8_GrPixelConfig;
 
     sk_sp<GrTexture> result(texProvider->createApproxTexture(desc));
diff --git a/src/gpu/GrClipStackClip.h b/src/gpu/GrClipStackClip.h
index 38f9e2d..aaa2f90 100644
--- a/src/gpu/GrClipStackClip.h
+++ b/src/gpu/GrClipStackClip.h
@@ -52,28 +52,19 @@
     // Draws the clip into the stencil buffer
     static bool CreateStencilClipMask(GrContext*,
                                       GrDrawContext*,
-                                      int32_t elementsGenID,
-                                      GrReducedClip::InitialState initialState,
-                                      const GrReducedClip::ElementList& elements,
-                                      const SkIRect& clipSpaceIBounds,
+                                      const GrReducedClip&,
                                       const SkIPoint& clipSpaceToStencilOffset);
 
     // Creates an alpha mask of the clip. The mask is a rasterization of elements through the
     // rect specified by clipSpaceIBounds.
     static sk_sp<GrTexture> CreateAlphaClipMask(GrContext*,
-                                                int32_t elementsGenID,
-                                                GrReducedClip::InitialState initialState,
-                                                const GrReducedClip::ElementList& elements,
-                                                const SkVector& clipToMaskOffset,
-                                                const SkIRect& clipSpaceIBounds);
+                                                const GrReducedClip&,
+                                                const SkVector& clipToMaskOffset);
 
     // Similar to createAlphaClipMask but it rasterizes in SW and uploads to the result texture.
     static sk_sp<GrTexture> CreateSoftwareClipMask(GrTextureProvider*,
-                                                   int32_t elementsGenID,
-                                                   GrReducedClip::InitialState initialState,
-                                                   const GrReducedClip::ElementList& elements,
-                                                   const SkVector& clipToMaskOffset,
-                                                   const SkIRect& clipSpaceIBounds);
+                                                   const GrReducedClip&,
+                                                   const SkVector& clipToMaskOffset);
 
    static bool UseSWOnlyPath(GrContext*,
                              bool hasUserStencilSettings,
diff --git a/src/gpu/GrReducedClip.cpp b/src/gpu/GrReducedClip.cpp
index 2940f6a..f7bac4a 100644
--- a/src/gpu/GrReducedClip.cpp
+++ b/src/gpu/GrReducedClip.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright 2012 Google Inc.
+ * Copyright 2016 Google Inc.
  *
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
@@ -23,9 +23,11 @@
     //  b) an operation that is known to make the bounds all inside/outside
     //  c) a replace operation
 
-    static const GrReducedClip::InitialState kUnknown_InitialState =
-        static_cast<GrReducedClip::InitialState>(-1);
-    GrReducedClip::InitialState initialState = kUnknown_InitialState;
+    enum class InitialTriState {
+        kUnknown = -1,
+        kAllIn = (int)GrReducedClip::InitialState::kAllIn,
+        kAllOut = (int)GrReducedClip::InitialState::kAllOut
+    } initialState = InitialTriState::kUnknown;
 
     // During our backwards walk, track whether we've seen ops that either grow or shrink the clip.
     // TODO: track these per saved clip so that we can consider them on the forward pass.
@@ -39,18 +41,18 @@
 
     SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
     int numAAElements = 0;
-    while (kUnknown_InitialState == initialState) {
+    while (InitialTriState::kUnknown == initialState) {
         const Element* element = iter.prev();
         if (nullptr == element) {
-            initialState = GrReducedClip::kAllIn_InitialState;
+            initialState = InitialTriState::kAllIn;
             break;
         }
         if (SkClipStack::kEmptyGenID == element->getGenID()) {
-            initialState = GrReducedClip::kAllOut_InitialState;
+            initialState = InitialTriState::kAllOut;
             break;
         }
         if (SkClipStack::kWideOpenGenID == element->getGenID()) {
-            initialState = GrReducedClip::kAllIn_InitialState;
+            initialState = InitialTriState::kAllIn;
             break;
         }
 
@@ -65,12 +67,12 @@
                     if (element->contains(relaxedQueryBounds)) {
                         skippable = true;
                     } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
-                        initialState = GrReducedClip::kAllOut_InitialState;
+                        initialState = InitialTriState::kAllOut;
                         skippable = true;
                     }
                 } else {
                     if (element->contains(relaxedQueryBounds)) {
-                        initialState = GrReducedClip::kAllOut_InitialState;
+                        initialState = InitialTriState::kAllOut;
                         skippable = true;
                     } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
                         skippable = true;
@@ -86,7 +88,7 @@
                 // empty.
                 if (element->isInverseFilled()) {
                     if (element->contains(relaxedQueryBounds)) {
-                        initialState = GrReducedClip::kAllOut_InitialState;
+                        initialState = InitialTriState::kAllOut;
                         skippable = true;
                     } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
                         skippable = true;
@@ -95,7 +97,7 @@
                     if (element->contains(relaxedQueryBounds)) {
                         skippable = true;
                     } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
-                        initialState = GrReducedClip::kAllOut_InitialState;
+                        initialState = InitialTriState::kAllOut;
                         skippable = true;
                     }
                 }
@@ -111,12 +113,12 @@
                     if (element->contains(relaxedQueryBounds)) {
                         skippable = true;
                     } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
-                        initialState = GrReducedClip::kAllIn_InitialState;
+                        initialState = InitialTriState::kAllIn;
                         skippable = true;
                     }
                 } else {
                     if (element->contains(relaxedQueryBounds)) {
-                        initialState = GrReducedClip::kAllIn_InitialState;
+                        initialState = InitialTriState::kAllIn;
                         skippable = true;
                     } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
                         skippable = true;
@@ -155,7 +157,7 @@
                 // all outside the current clip.B
                 if (element->isInverseFilled()) {
                     if (element->contains(relaxedQueryBounds)) {
-                        initialState = GrReducedClip::kAllOut_InitialState;
+                        initialState = InitialTriState::kAllOut;
                         skippable = true;
                     } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
                         isFlip = true;
@@ -164,7 +166,7 @@
                     if (element->contains(relaxedQueryBounds)) {
                         isFlip = true;
                     } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
-                        initialState = GrReducedClip::kAllOut_InitialState;
+                        initialState = InitialTriState::kAllOut;
                         skippable = true;
                     }
                 }
@@ -180,23 +182,23 @@
                 // setting the correct value for initialState.
                 if (element->isInverseFilled()) {
                     if (element->contains(relaxedQueryBounds)) {
-                        initialState = GrReducedClip::kAllOut_InitialState;
+                        initialState = InitialTriState::kAllOut;
                         skippable = true;
                     } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
-                        initialState = GrReducedClip::kAllIn_InitialState;
+                        initialState = InitialTriState::kAllIn;
                         skippable = true;
                     }
                 } else {
                     if (element->contains(relaxedQueryBounds)) {
-                        initialState = GrReducedClip::kAllIn_InitialState;
+                        initialState = InitialTriState::kAllIn;
                         skippable = true;
                     } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
-                        initialState = GrReducedClip::kAllOut_InitialState;
+                        initialState = InitialTriState::kAllOut;
                         skippable = true;
                     }
                 }
                 if (!skippable) {
-                    initialState = GrReducedClip::kAllOut_InitialState;
+                    initialState = InitialTriState::kAllOut;
                     embiggens = emsmallens = true;
                 }
                 break;
@@ -230,16 +232,16 @@
                     newElement->invertShapeFillType();
                     newElement->setOp(SkRegion::kDifference_Op);
                     if (isReplace) {
-                        SkASSERT(GrReducedClip::kAllOut_InitialState == initialState);
-                        initialState = GrReducedClip::kAllIn_InitialState;
+                        SkASSERT(InitialTriState::kAllOut == initialState);
+                        initialState = InitialTriState::kAllIn;
                     }
                 }
             }
         }
     }
 
-    if ((GrReducedClip::kAllOut_InitialState == initialState && !embiggens) ||
-        (GrReducedClip::kAllIn_InitialState == initialState && !emsmallens)) {
+    if ((InitialTriState::kAllOut == initialState && !embiggens) ||
+        (InitialTriState::kAllIn == initialState && !emsmallens)) {
         result->reset();
         numAAElements = 0;
     } else {
@@ -249,20 +251,20 @@
             switch (element->getOp()) {
                 case SkRegion::kDifference_Op:
                     // subtracting from the empty set yields the empty set.
-                    skippable = GrReducedClip::kAllOut_InitialState == initialState;
+                    skippable = InitialTriState::kAllOut == initialState;
                     break;
                 case SkRegion::kIntersect_Op:
                     // intersecting with the empty set yields the empty set
-                    if (GrReducedClip::kAllOut_InitialState == initialState) {
+                    if (InitialTriState::kAllOut == initialState) {
                         skippable = true;
                     } else {
                         // We can clear to zero and then simply draw the clip element.
-                        initialState = GrReducedClip::kAllOut_InitialState;
+                        initialState = InitialTriState::kAllOut;
                         element->setOp(SkRegion::kReplace_Op);
                     }
                     break;
                 case SkRegion::kUnion_Op:
-                    if (GrReducedClip::kAllIn_InitialState == initialState) {
+                    if (InitialTriState::kAllIn == initialState) {
                         // unioning the infinite plane with anything is a no-op.
                         skippable = true;
                     } else {
@@ -271,23 +273,23 @@
                     }
                     break;
                 case SkRegion::kXOR_Op:
-                    if (GrReducedClip::kAllOut_InitialState == initialState) {
+                    if (InitialTriState::kAllOut == initialState) {
                         // xor could be changed to diff in the kAllIn case, not sure it's a win.
                         element->setOp(SkRegion::kReplace_Op);
                     }
                     break;
                 case SkRegion::kReverseDifference_Op:
-                    if (GrReducedClip::kAllIn_InitialState == initialState) {
+                    if (InitialTriState::kAllIn == initialState) {
                         // subtracting the whole plane will yield the empty set.
                         skippable = true;
-                        initialState = GrReducedClip::kAllOut_InitialState;
+                        initialState = InitialTriState::kAllOut;
                     } else {
                         // this picks up flips inserted in the backwards pass.
                         skippable = element->isInverseFilled() ?
                             GrClip::IsOutsideClip(element->getBounds(), queryBounds) :
                             element->contains(relaxedQueryBounds);
                         if (skippable) {
-                            initialState = GrReducedClip::kAllIn_InitialState;
+                            initialState = InitialTriState::kAllIn;
                         } else {
                             element->setOp(SkRegion::kReplace_Op);
                         }
@@ -315,7 +317,7 @@
     *requiresAA = numAAElements > 0;
 
     if (0 == result->count()) {
-        if (initialState == GrReducedClip::kAllIn_InitialState) {
+        if (initialState == InitialTriState::kAllIn) {
             *resultGenID = SkClipStack::kWideOpenGenID;
         } else {
             *resultGenID = SkClipStack::kEmptyGenID;
@@ -323,7 +325,8 @@
     }
 
     SkASSERT(SkClipStack::kInvalidGenID != *resultGenID);
-    return initialState;
+    SkASSERT(InitialTriState::kUnknown != initialState);
+    return static_cast<GrReducedClip::InitialState>(initialState);
 }
 
 /*
@@ -333,25 +336,20 @@
 based on later intersect operations, and perhaps remove intersect-rects. We could optionally
 take a rect in case the caller knows a bound on what is to be drawn through this clip.
 */
-GrReducedClip::InitialState GrReducedClip::ReduceClipStack(const SkClipStack& stack,
-                                                           const SkRect& queryBounds,
-                                                           ElementList* result,
-                                                           int32_t* resultGenID,
-                                                           SkIRect* clipIBounds,
-                                                           bool* requiresAA) {
+GrReducedClip::GrReducedClip(const SkClipStack& stack, const SkRect& queryBounds) {
     SkASSERT(!queryBounds.isEmpty());
-    result->reset();
 
     // The clip established by the element list might be cached based on the last
     // generation id. When we make early returns, we do not know what was the generation
     // id that lead to the state. Make a conservative guess.
-    *resultGenID = stack.getTopmostGenID();
+    fGenID = stack.getTopmostGenID();
 
     // TODO: instead devise a way of telling the caller to disregard some or all of the clip bounds.
-    *clipIBounds = GrClip::GetPixelIBounds(queryBounds);
+    fIBounds = GrClip::GetPixelIBounds(queryBounds);
 
     if (stack.isWideOpen()) {
-        return kAllIn_InitialState;
+        fInitialState = InitialState::kAllIn;
+        return;
     }
 
     SkClipStack::BoundsType stackBoundsType;
@@ -361,7 +359,8 @@
 
     if (stackBounds.isEmpty() || GrClip::IsOutsideClip(stackBounds, queryBounds)) {
         bool insideOut = SkClipStack::kInsideOut_BoundsType == stackBoundsType;
-        return insideOut ? kAllIn_InitialState : kAllOut_InitialState;
+        fInitialState = insideOut ? InitialState::kAllIn : InitialState::kAllOut;
+        return;
     }
 
     if (iior) {
@@ -371,31 +370,39 @@
         SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
         if (!iter.prev()->isAA() || GrClip::IsPixelAligned(stackBounds)) {
             // The clip is a non-aa rect. This is the one spot where we can actually implement the
-            // clip (using clipIBounds) rather than just telling the caller what it should be.
-            stackBounds.round(clipIBounds);
-            return kAllIn_InitialState;
+            // clip (using fIBounds) rather than just telling the caller what it should be.
+            stackBounds.round(&fIBounds);
+            fInitialState = fIBounds.isEmpty() ? InitialState::kAllOut : InitialState::kAllIn;
+            return;
         }
         if (GrClip::IsInsideClip(stackBounds, queryBounds)) {
-            return kAllIn_InitialState;
+            fInitialState = InitialState::kAllIn;
+            return;
         }
 
-        // Implement the clip with an AA rect element.
-        result->addToHead(stackBounds, SkRegion::kReplace_Op, true/*doAA*/);
-        *requiresAA = true;
+        SkAssertResult(fIBounds.intersect(GrClip::GetPixelIBounds(stackBounds)));
+        SkASSERT(!fIBounds.isEmpty()); // Empty should have been blocked by IsOutsideClip above.
 
-        SkAssertResult(clipIBounds->intersect(GrClip::GetPixelIBounds(stackBounds)));
-        return kAllOut_InitialState;
+        // Implement the clip with an AA rect element.
+        fElements.addToHead(stackBounds, SkRegion::kReplace_Op, true/*doAA*/);
+        fRequiresAA = true;
+
+        fInitialState = InitialState::kAllOut;
+        return;
     }
 
     SkRect tighterQuery = queryBounds;
     if (SkClipStack::kNormal_BoundsType == stackBoundsType) {
         // Tighten the query by introducing a new clip at the stack's pixel boundaries. (This new
-        // clip will be enforced by the scissor through clipIBounds.)
+        // clip will be enforced by the scissor through fIBounds.)
         SkAssertResult(tighterQuery.intersect(GrClip::GetPixelBounds(stackBounds)));
-        *clipIBounds = GrClip::GetPixelIBounds(tighterQuery);
+        fIBounds = GrClip::GetPixelIBounds(tighterQuery);
     }
 
+    SkASSERT(!fIBounds.isEmpty()); // Empty should have been blocked by IsOutsideClip above.
+
     // Now that we have determined the bounds to use and filtered out the trivial cases, call the
     // helper that actually walks the stack.
-    return reduced_stack_walker(stack, tighterQuery, *clipIBounds, result, resultGenID, requiresAA);
+    fInitialState = reduced_stack_walker(stack, tighterQuery, fIBounds, &fElements, &fGenID,
+                                         &fRequiresAA);
 }
diff --git a/src/gpu/GrReducedClip.h b/src/gpu/GrReducedClip.h
index 780f6f1..07e0669 100644
--- a/src/gpu/GrReducedClip.h
+++ b/src/gpu/GrReducedClip.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2012 Google Inc.
+ * Copyright 2016 Google Inc.
  *
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
@@ -11,37 +11,57 @@
 #include "SkClipStack.h"
 #include "SkTLList.h"
 
+/**
+ * This class takes a clip stack and produces a reduced set of elements that are equivalent to
+ * applying that full stack within a specified query rectangle.
+ */
 class SK_API GrReducedClip {
 public:
+    GrReducedClip(const SkClipStack& stack, const SkRect& queryBounds);
+
+    /**
+     * Uniquely identifies this reduced clip.
+     */
+    int32_t genID() const { return fGenID; }
+
+    /**
+     * Bounding box within which the reduced clip is valid. The caller must not draw any pixels
+     * outside this box.
+     */
+    const SkIRect& iBounds() const { return fIBounds; }
+    int left() const { return this->iBounds().left(); }
+    int top() const { return this->iBounds().top(); }
+    int width() const { return this->iBounds().width(); }
+    int height() const { return this->iBounds().height(); }
+
     typedef SkTLList<SkClipStack::Element, 16> ElementList;
 
-    enum InitialState {
-        kAllIn_InitialState,
-        kAllOut_InitialState,
+    /**
+     * Populated with a minimal list of elements that implement the clip.
+     */
+    const ElementList& elements() const { return fElements; }
+
+    /**
+     * Indicates whether antialiasing is required to process any of the clip elements.
+     */
+    bool requiresAA() const { return fRequiresAA; }
+
+    enum class InitialState : bool {
+        kAllIn,
+        kAllOut
     };
 
     /**
-     * This function produces a reduced set of SkClipStack::Elements that are equivalent to applying
-     * a full clip stack within a specified query rectangle.
-     *
-     * @param stack          the clip stack to reduce.
-     * @param queryBounds    bounding box of geometry the stack will clip.
-     * @param result         populated with a minimal list of elements that implement the clip
-     *                       within the provided query bounds.
-     * @param resultGenID    uniquely identifies the resulting reduced clip.
-     * @param clipIBounds    bounding box within which the reduced clip is valid. The caller must
-     *                       not draw any pixels outside this box. NOTE: this box may be undefined
-     *                       if no pixels are valid (e.g. empty result, "all out" initial state.)
-     * @param requiresAA     indicates whether anti-aliasing is required to process any of the
-     *                       elements in the element list result. Undefined if the result is empty.
-     * @return the initial clip state within clipIBounds ("all in" or "all out").
+     * The initial state of the clip within iBounds().
      */
-    static InitialState ReduceClipStack(const SkClipStack& stack,
-                                        const SkRect& queryBounds,
-                                        ElementList* result,
-                                        int32_t* resultGenID,
-                                        SkIRect* clipIBounds,
-                                        bool* requiresAA);
+    InitialState initialState() const { return fInitialState; }
+
+private:
+    int32_t        fGenID;
+    SkIRect        fIBounds;
+    ElementList    fElements;
+    bool           fRequiresAA;
+    InitialState   fInitialState;
 };
 
 #endif
diff --git a/src/utils/SkLua.cpp b/src/utils/SkLua.cpp
index 3a335ad..8ea9eee 100644
--- a/src/utils/SkLua.cpp
+++ b/src/utils/SkLua.cpp
@@ -644,22 +644,9 @@
 #if SK_SUPPORT_GPU
     const SkCanvas* canvas = get_ref<SkCanvas>(L, 1);
     SkRect queryBounds = SkRect::Make(canvas->getTopLayerBounds());
+    const GrReducedClip reducedClip(*canvas->getClipStack(), queryBounds);
 
-    GrReducedClip::ElementList elements;
-    int32_t genID;
-    SkIRect resultBounds;
-    bool requiresAA;
-
-    const SkClipStack& stack = *canvas->getClipStack();
-
-    GrReducedClip::ReduceClipStack(stack,
-                                   queryBounds,
-                                   &elements,
-                                   &genID,
-                                   &resultBounds,
-                                   &requiresAA);
-
-    GrReducedClip::ElementList::Iter iter(elements);
+    GrReducedClip::ElementList::Iter iter(reducedClip.elements());
     int i = 0;
     lua_newtable(L);
     while(iter.get()) {