ccpr: Convert GrCCDrawPathsOp::SingleDraw to a class

Just some cleanup.

Bug: skia:8462
Change-Id: Iad1d3111690c1db1c36a495b4badbe9edd4b5555
Reviewed-on: https://skia-review.googlesource.com/c/180740
Commit-Queue: Chris Dalton <csmartdalton@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/src/gpu/ccpr/GrCCDrawPathsOp.cpp b/src/gpu/ccpr/GrCCDrawPathsOp.cpp
index ba16f58..b9f0969 100644
--- a/src/gpu/ccpr/GrCCDrawPathsOp.cpp
+++ b/src/gpu/ccpr/GrCCDrawPathsOp.cpp
@@ -161,39 +161,42 @@
 GrDrawOp::RequiresDstTexture GrCCDrawPathsOp::finalize(const GrCaps& caps,
                                                        const GrAppliedClip* clip) {
     SkASSERT(1 == fNumDraws);  // There should only be one single path draw in this Op right now.
-    SingleDraw* draw = &fDraws.head();
+    return fDraws.head().finalize(caps, clip, &fProcessors);
+}
 
-    const GrProcessorSet::Analysis& analysis = fProcessors.finalize(
-            draw->fColor, GrProcessorAnalysisCoverage::kSingleChannel, clip, false, caps,
-            &draw->fColor);
+GrDrawOp::RequiresDstTexture GrCCDrawPathsOp::SingleDraw::finalize(
+        const GrCaps& caps, const GrAppliedClip* clip, GrProcessorSet* processors) {
+    const GrProcessorSet::Analysis& analysis = processors->finalize(
+            fColor, GrProcessorAnalysisCoverage::kSingleChannel, clip, false, caps,
+            &fColor);
 
     // Lines start looking jagged when they get thinner than 1px. For thin strokes it looks better
     // if we can convert them to hairline (i.e., inflate the stroke width to 1px), and instead
     // reduce the opacity to create the illusion of thin-ness. This strategy also helps reduce
     // artifacts from coverage dilation when there are self intersections.
     if (analysis.isCompatibleWithCoverageAsAlpha() &&
-            !draw->fShape.style().strokeRec().isFillStyle() && draw->fStrokeDevWidth < 1) {
+            !fShape.style().strokeRec().isFillStyle() && fStrokeDevWidth < 1) {
         // Modifying the shape affects its cache key. The draw can't have a cache entry yet or else
         // our next step would invalidate it.
-        SkASSERT(!draw->fCacheEntry);
-        SkASSERT(SkStrokeRec::kStroke_Style == draw->fShape.style().strokeRec().getStyle());
+        SkASSERT(!fCacheEntry);
+        SkASSERT(SkStrokeRec::kStroke_Style == fShape.style().strokeRec().getStyle());
 
         SkPath path;
-        draw->fShape.asPath(&path);
+        fShape.asPath(&path);
 
         // Create a hairline version of our stroke.
-        SkStrokeRec hairlineStroke = draw->fShape.style().strokeRec();
+        SkStrokeRec hairlineStroke = fShape.style().strokeRec();
         hairlineStroke.setStrokeStyle(0);
 
         // How transparent does a 1px stroke have to be in order to appear as thin as the real one?
-        float coverage = draw->fStrokeDevWidth;
+        float coverage = fStrokeDevWidth;
 
-        draw->fShape = GrShape(path, GrStyle(hairlineStroke, nullptr));
-        draw->fStrokeDevWidth = 1;
+        fShape = GrShape(path, GrStyle(hairlineStroke, nullptr));
+        fStrokeDevWidth = 1;
 
         // TODO4F: Preserve float colors
         // fShapeConservativeIBounds already accounted for this possibility of inflating the stroke.
-        draw->fColor = draw->fColor * coverage;
+        fColor = fColor * coverage;
     }
 
     return RequiresDstTexture(analysis.requiresDstTexture());
@@ -228,136 +231,78 @@
 void GrCCDrawPathsOp::accountForOwnPaths(GrCCPathCache* pathCache,
                                          GrOnFlushResourceProvider* onFlushRP,
                                          GrCCPerFlushResourceSpecs* specs) {
+    for (SingleDraw& draw : fDraws) {
+        draw.accountForOwnPath(pathCache, onFlushRP, specs);
+    }
+}
+
+void GrCCDrawPathsOp::SingleDraw::accountForOwnPath(
+        GrCCPathCache* pathCache, GrOnFlushResourceProvider* onFlushRP,
+        GrCCPerFlushResourceSpecs* specs) {
     using CreateIfAbsent = GrCCPathCache::CreateIfAbsent;
     using MaskTransform = GrCCPathCache::MaskTransform;
     using CoverageType = GrCCAtlas::CoverageType;
 
-    for (SingleDraw& draw : fDraws) {
-        SkPath path;
-        draw.fShape.asPath(&path);
+    SkPath path;
+    fShape.asPath(&path);
 
-        SkASSERT(!draw.fCacheEntry);
+    SkASSERT(!fCacheEntry);
 
-        if (pathCache) {
-            MaskTransform m(draw.fMatrix, &draw.fCachedMaskShift);
-            bool canStashPathMask = draw.fMaskVisibility >= Visibility::kMostlyComplete;
-            draw.fCacheEntry =
-                    pathCache->find(onFlushRP, draw.fShape, m, CreateIfAbsent(canStashPathMask));
-        }
-
-        if (draw.fCacheEntry) {
-            if (const GrCCCachedAtlas* cachedAtlas = draw.fCacheEntry->cachedAtlas()) {
-                SkASSERT(cachedAtlas->getOnFlushProxy());
-                if (CoverageType::kA8_LiteralCoverage == cachedAtlas->coverageType()) {
-                    ++specs->fNumCachedPaths;
-                } else {
-                    // Suggest that this path be copied to a literal coverage atlas, to save memory.
-                    // (The client may decline this copy via DoCopiesToA8Coverage::kNo.)
-                    int idx = (draw.fShape.style().strokeRec().isFillStyle())
-                            ? GrCCPerFlushResourceSpecs::kFillIdx
-                            : GrCCPerFlushResourceSpecs::kStrokeIdx;
-                    ++specs->fNumCopiedPaths[idx];
-                    specs->fCopyPathStats[idx].statPath(path);
-                    specs->fCopyAtlasSpecs.accountForSpace(
-                            draw.fCacheEntry->width(), draw.fCacheEntry->height());
-                    draw.fDoCopyToA8Coverage = true;
-                }
-                continue;
-            }
-
-            if (Visibility::kMostlyComplete == draw.fMaskVisibility &&
-                    draw.fCacheEntry->hitCount() > 1) {
-                int shapeSize = SkTMax(draw.fShapeConservativeIBounds.height(),
-                                       draw.fShapeConservativeIBounds.width());
-                if (shapeSize <= onFlushRP->caps()->maxRenderTargetSize()) {
-                    // We've seen this path before with a compatible matrix, and it's mostly
-                    // visible. Just render the whole mask so we can try to cache it.
-                    draw.fMaskDevIBounds = draw.fShapeConservativeIBounds;
-                    draw.fMaskVisibility = Visibility::kComplete;
-                }
-            }
-        }
-
-        int idx = (draw.fShape.style().strokeRec().isFillStyle())
-                ? GrCCPerFlushResourceSpecs::kFillIdx
-                : GrCCPerFlushResourceSpecs::kStrokeIdx;
-        ++specs->fNumRenderedPaths[idx];
-        specs->fRenderedPathStats[idx].statPath(path);
-        specs->fRenderedAtlasSpecs.accountForSpace(draw.fMaskDevIBounds.width(),
-                                                   draw.fMaskDevIBounds.height());
+    if (pathCache) {
+        MaskTransform m(fMatrix, &fCachedMaskShift);
+        bool canStashPathMask = fMaskVisibility >= Visibility::kMostlyComplete;
+        fCacheEntry = pathCache->find(onFlushRP, fShape, m, CreateIfAbsent(canStashPathMask));
     }
+
+    if (fCacheEntry) {
+        if (const GrCCCachedAtlas* cachedAtlas = fCacheEntry->cachedAtlas()) {
+            SkASSERT(cachedAtlas->getOnFlushProxy());
+            if (CoverageType::kA8_LiteralCoverage == cachedAtlas->coverageType()) {
+                ++specs->fNumCachedPaths;
+            } else {
+                // Suggest that this path be copied to a literal coverage atlas, to save memory.
+                // (The client may decline this copy via DoCopiesToA8Coverage::kNo.)
+                int idx = (fShape.style().strokeRec().isFillStyle())
+                        ? GrCCPerFlushResourceSpecs::kFillIdx
+                        : GrCCPerFlushResourceSpecs::kStrokeIdx;
+                ++specs->fNumCopiedPaths[idx];
+                specs->fCopyPathStats[idx].statPath(path);
+                specs->fCopyAtlasSpecs.accountForSpace(fCacheEntry->width(), fCacheEntry->height());
+                fDoCopyToA8Coverage = true;
+            }
+            return;
+        }
+
+        if (Visibility::kMostlyComplete == fMaskVisibility && fCacheEntry->hitCount() > 1) {
+            int shapeSize = SkTMax(fShapeConservativeIBounds.height(),
+                                   fShapeConservativeIBounds.width());
+            if (shapeSize <= onFlushRP->caps()->maxRenderTargetSize()) {
+                // We've seen this path before with a compatible matrix, and it's mostly
+                // visible. Just render the whole mask so we can try to cache it.
+                fMaskDevIBounds = fShapeConservativeIBounds;
+                fMaskVisibility = Visibility::kComplete;
+            }
+        }
+    }
+
+    int idx = (fShape.style().strokeRec().isFillStyle())
+            ? GrCCPerFlushResourceSpecs::kFillIdx
+            : GrCCPerFlushResourceSpecs::kStrokeIdx;
+    ++specs->fNumRenderedPaths[idx];
+    specs->fRenderedPathStats[idx].statPath(path);
+    specs->fRenderedAtlasSpecs.accountForSpace(fMaskDevIBounds.width(),
+                                               fMaskDevIBounds.height());
 }
 
 void GrCCDrawPathsOp::setupResources(
         GrCCPathCache* pathCache, GrOnFlushResourceProvider* onFlushRP,
         GrCCPerFlushResources* resources, DoCopiesToA8Coverage doCopies) {
-    using DoEvenOddFill = GrCCPathProcessor::DoEvenOddFill;
     SkASSERT(fNumDraws > 0);
     SkASSERT(-1 == fBaseInstance);
     fBaseInstance = resources->nextPathInstanceIdx();
 
     for (SingleDraw& draw : fDraws) {
-        SkPath path;
-        draw.fShape.asPath(&path);
-
-        auto doEvenOddFill = DoEvenOddFill(draw.fShape.style().strokeRec().isFillStyle() &&
-                                           SkPath::kEvenOdd_FillType == path.getFillType());
-        SkASSERT(SkPath::kEvenOdd_FillType == path.getFillType() ||
-                 SkPath::kWinding_FillType == path.getFillType());
-
-        if (auto cacheEntry = draw.fCacheEntry.get()) {
-            // Does the path already exist in a cached atlas texture?
-            if (cacheEntry->cachedAtlas()) {
-                SkASSERT(cacheEntry->cachedAtlas()->getOnFlushProxy());
-                if (DoCopiesToA8Coverage::kYes == doCopies && draw.fDoCopyToA8Coverage) {
-                    resources->upgradeEntryToLiteralCoverageAtlas(pathCache, onFlushRP, cacheEntry,
-                                                                  doEvenOddFill);
-                    SkASSERT(cacheEntry->cachedAtlas());
-                    SkASSERT(GrCCAtlas::CoverageType::kA8_LiteralCoverage
-                                     == cacheEntry->cachedAtlas()->coverageType());
-                    SkASSERT(cacheEntry->cachedAtlas()->getOnFlushProxy());
-                }
-                this->recordInstance(cacheEntry->cachedAtlas()->getOnFlushProxy(),
-                                     resources->nextPathInstanceIdx());
-                // TODO4F: Preserve float colors
-                resources->appendDrawPathInstance().set(*cacheEntry, draw.fCachedMaskShift,
-                                                        draw.fColor.toBytes_RGBA());
-                continue;
-            }
-        }
-
-        // Render the raw path into a coverage count atlas. renderShapeInAtlas() gives us two tight
-        // bounding boxes: One in device space, as well as a second one rotated an additional 45
-        // degrees. The path vertex shader uses these two bounding boxes to generate an octagon that
-        // circumscribes the path.
-        SkRect devBounds, devBounds45;
-        SkIRect devIBounds;
-        SkIVector devToAtlasOffset;
-        if (auto atlas = resources->renderShapeInAtlas(
-                    draw.fMaskDevIBounds, draw.fMatrix, draw.fShape, draw.fStrokeDevWidth,
-                    &devBounds, &devBounds45, &devIBounds, &devToAtlasOffset)) {
-            this->recordInstance(atlas->textureProxy(), resources->nextPathInstanceIdx());
-            // TODO4F: Preserve float colors
-            resources->appendDrawPathInstance().set(devBounds, devBounds45, devToAtlasOffset,
-                                                    draw.fColor.toBytes_RGBA(), doEvenOddFill);
-
-            // If we have a spot in the path cache, try to make a note of where this mask is so we
-            // can reuse it in the future.
-            if (auto cacheEntry = draw.fCacheEntry.get()) {
-                SkASSERT(!cacheEntry->cachedAtlas());
-
-                if (Visibility::kComplete != draw.fMaskVisibility || cacheEntry->hitCount() <= 1) {
-                    // Don't cache a path mask unless it's completely visible with a hit count > 1.
-                    //
-                    // NOTE: mostly-visible paths with a hit count > 1 should have been promoted to
-                    // fully visible during accountForOwnPaths().
-                    continue;
-                }
-
-                cacheEntry->setCoverageCountAtlas(onFlushRP, atlas, devToAtlasOffset, devBounds,
-                                                  devBounds45, devIBounds, draw.fCachedMaskShift);
-            }
-        }
+        draw.setupResources(pathCache, onFlushRP, resources, doCopies, this);
     }
 
     if (!fInstanceRanges.empty()) {
@@ -365,6 +310,74 @@
     }
 }
 
+void GrCCDrawPathsOp::SingleDraw::setupResources(
+        GrCCPathCache* pathCache, GrOnFlushResourceProvider* onFlushRP,
+        GrCCPerFlushResources* resources, DoCopiesToA8Coverage doCopies, GrCCDrawPathsOp* op) {
+    using DoEvenOddFill = GrCCPathProcessor::DoEvenOddFill;
+
+    SkPath path;
+    fShape.asPath(&path);
+
+    auto doEvenOddFill = DoEvenOddFill(fShape.style().strokeRec().isFillStyle() &&
+                                       SkPath::kEvenOdd_FillType == path.getFillType());
+    SkASSERT(SkPath::kEvenOdd_FillType == path.getFillType() ||
+             SkPath::kWinding_FillType == path.getFillType());
+
+    if (fCacheEntry) {
+        // Does the path already exist in a cached atlas texture?
+        if (fCacheEntry->cachedAtlas()) {
+            SkASSERT(fCacheEntry->cachedAtlas()->getOnFlushProxy());
+            if (DoCopiesToA8Coverage::kYes == doCopies && fDoCopyToA8Coverage) {
+                resources->upgradeEntryToLiteralCoverageAtlas(pathCache, onFlushRP,
+                                                              fCacheEntry.get(), doEvenOddFill);
+                SkASSERT(fCacheEntry->cachedAtlas());
+                SkASSERT(GrCCAtlas::CoverageType::kA8_LiteralCoverage
+                                 == fCacheEntry->cachedAtlas()->coverageType());
+                SkASSERT(fCacheEntry->cachedAtlas()->getOnFlushProxy());
+            }
+            op->recordInstance(fCacheEntry->cachedAtlas()->getOnFlushProxy(),
+                               resources->nextPathInstanceIdx());
+            // TODO4F: Preserve float colors
+            resources->appendDrawPathInstance().set(*fCacheEntry, fCachedMaskShift,
+                                                    fColor.toBytes_RGBA());
+            return;
+        }
+    }
+
+    // Render the raw path into a coverage count atlas. renderShapeInAtlas() gives us two tight
+    // bounding boxes: One in device space, as well as a second one rotated an additional 45
+    // degrees. The path vertex shader uses these two bounding boxes to generate an octagon that
+    // circumscribes the path.
+    SkRect devBounds, devBounds45;
+    SkIRect devIBounds;
+    SkIVector devToAtlasOffset;
+    if (auto atlas = resources->renderShapeInAtlas(
+                fMaskDevIBounds, fMatrix, fShape, fStrokeDevWidth, &devBounds, &devBounds45,
+                &devIBounds, &devToAtlasOffset)) {
+        op->recordInstance(atlas->textureProxy(), resources->nextPathInstanceIdx());
+        // TODO4F: Preserve float colors
+        resources->appendDrawPathInstance().set(devBounds, devBounds45, devToAtlasOffset,
+                                                fColor.toBytes_RGBA(), doEvenOddFill);
+
+        // If we have a spot in the path cache, try to make a note of where this mask is so we
+        // can reuse it in the future.
+        if (fCacheEntry) {
+            SkASSERT(!fCacheEntry->cachedAtlas());
+
+            if (Visibility::kComplete != fMaskVisibility || fCacheEntry->hitCount() <= 1) {
+                // Don't cache a path mask unless it's completely visible with a hit count > 1.
+                //
+                // NOTE: mostly-visible paths with a hit count > 1 should have been promoted to
+                // fully visible during accountForOwnPaths().
+                return;
+            }
+
+            fCacheEntry->setCoverageCountAtlas(onFlushRP, atlas, devToAtlasOffset, devBounds,
+                                               devBounds45, devIBounds, fCachedMaskShift);
+        }
+    }
+}
+
 inline void GrCCDrawPathsOp::recordInstance(GrTextureProxy* atlasProxy, int instanceIdx) {
     if (fInstanceRanges.empty()) {
         fInstanceRanges.push_back({atlasProxy, instanceIdx});