Move SkGpuDevice::internalDrawPath to GrBlurUtils::drawPathWithMaskFilter

This CL is ugly but it:

removes the stored SkGpuDevice back pointer from GrTextContext (at the cost of passing more parameters)
moves SkGpuDevice::internalDrawPath to GrDrawContext::drawPathFull

Unfortunately, the GrTextContext-derived classes still need the SkGpuDevice for filterTextFlags calls but I will try removing that in a separate CL.

Review URL: https://codereview.chromium.org/1157773003
diff --git a/src/gpu/GrBlurUtils.cpp b/src/gpu/GrBlurUtils.cpp
new file mode 100644
index 0000000..a757a4a
--- /dev/null
+++ b/src/gpu/GrBlurUtils.cpp
@@ -0,0 +1,295 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrBlurUtils.h"
+#include "GrDrawContext.h"
+#include "GrContext.h"
+#include "effects/GrSimpleTextureEffect.h"
+#include "GrStrokeInfo.h"
+#include "GrTexture.h"
+#include "GrTextureProvider.h"
+#include "SkDraw.h"
+#include "SkGr.h"
+#include "SkMaskFilter.h"
+#include "SkPaint.h"
+
+static bool clip_bounds_quick_reject(const SkIRect& clipBounds, const SkIRect& rect) {
+    return clipBounds.isEmpty() || rect.isEmpty() || !SkIRect::Intersects(clipBounds, rect);
+}
+
+// Draw a mask using the supplied paint. Since the coverage/geometry
+// is already burnt into the mask this boils down to a rect draw.
+// Return true if the mask was successfully drawn.
+static bool draw_mask(GrDrawContext* drawContext,
+                      GrRenderTarget* rt,
+                      const GrClip& clip,
+                      const SkMatrix& viewMatrix,
+                      const SkRect& maskRect,
+                      GrPaint* grp,
+                      GrTexture* mask) {
+    SkMatrix matrix;
+    matrix.setTranslate(-maskRect.fLeft, -maskRect.fTop);
+    matrix.postIDiv(mask->width(), mask->height());
+
+    grp->addCoverageProcessor(GrSimpleTextureEffect::Create(mask, matrix,
+                                                            kDevice_GrCoordSet))->unref();
+
+    SkMatrix inverse;
+    if (!viewMatrix.invert(&inverse)) {
+        return false;
+    }
+    drawContext->drawNonAARectWithLocalMatrix(rt, clip, *grp, SkMatrix::I(), maskRect, inverse);
+    return true;
+}
+
+static bool draw_with_mask_filter(GrDrawContext* drawContext,
+                                  GrTextureProvider* textureProvider,
+                                  GrRenderTarget* rt,
+                                  const GrClip& clipData,
+                                  const SkMatrix& viewMatrix,
+                                  const SkPath& devPath,
+                                  SkMaskFilter* filter,
+                                  const SkIRect& clipBounds,
+                                  GrPaint* grp,
+                                  SkPaint::Style style) {
+    SkMask  srcM, dstM;
+
+    if (!SkDraw::DrawToMask(devPath, &clipBounds, filter, &viewMatrix, &srcM,
+                            SkMask::kComputeBoundsAndRenderImage_CreateMode, style)) {
+        return false;
+    }
+    SkAutoMaskFreeImage autoSrc(srcM.fImage);
+
+    if (!filter->filterMask(&dstM, srcM, viewMatrix, NULL)) {
+        return false;
+    }
+    // this will free-up dstM when we're done (allocated in filterMask())
+    SkAutoMaskFreeImage autoDst(dstM.fImage);
+
+    if (clip_bounds_quick_reject(clipBounds, dstM.fBounds)) {
+        return false;
+    }
+
+    // we now have a device-aligned 8bit mask in dstM, ready to be drawn using
+    // the current clip (and identity matrix) and GrPaint settings
+    GrSurfaceDesc desc;
+    desc.fWidth = dstM.fBounds.width();
+    desc.fHeight = dstM.fBounds.height();
+    desc.fConfig = kAlpha_8_GrPixelConfig;
+
+    SkAutoTUnref<GrTexture> texture(textureProvider->refScratchTexture(
+        desc, GrTextureProvider::kApprox_ScratchTexMatch));
+    if (!texture) {
+        return false;
+    }
+    texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
+                               dstM.fImage, dstM.fRowBytes);
+
+    SkRect maskRect = SkRect::Make(dstM.fBounds);
+
+    return draw_mask(drawContext, rt, clipData, viewMatrix, maskRect, grp, texture);
+}
+
+// Create a mask of 'devPath' and place the result in 'mask'.
+static GrTexture* create_mask_GPU(GrContext* context,
+                                  const SkRect& maskRect,
+                                  const SkPath& devPath,
+                                  const GrStrokeInfo& strokeInfo,
+                                  bool doAA,
+                                  int sampleCnt) {
+    GrSurfaceDesc desc;
+    desc.fFlags = kRenderTarget_GrSurfaceFlag;
+    desc.fWidth = SkScalarCeilToInt(maskRect.width());
+    desc.fHeight = SkScalarCeilToInt(maskRect.height());
+    desc.fSampleCnt = doAA ? sampleCnt : 0;
+    // We actually only need A8, but it often isn't supported as a
+    // render target so default to RGBA_8888
+    desc.fConfig = kRGBA_8888_GrPixelConfig;
+
+    if (context->isConfigRenderable(kAlpha_8_GrPixelConfig, desc.fSampleCnt > 0)) {
+        desc.fConfig = kAlpha_8_GrPixelConfig;
+    }
+
+    GrTexture* mask = context->textureProvider()->refScratchTexture(
+        desc, GrTextureProvider::kApprox_ScratchTexMatch);
+    if (NULL == mask) {
+        return NULL;
+    }
+
+    SkRect clipRect = SkRect::MakeWH(maskRect.width(), maskRect.height());
+
+    GrDrawContext* drawContext = context->drawContext();
+    if (!drawContext) {
+        return NULL;
+    }
+
+    drawContext->clear(mask->asRenderTarget(), NULL, 0x0, true);
+
+    GrPaint tempPaint;
+    tempPaint.setAntiAlias(doAA);
+    tempPaint.setCoverageSetOpXPFactory(SkRegion::kReplace_Op);
+
+    // setup new clip
+    GrClip clip(clipRect);
+
+    // Draw the mask into maskTexture with the path's top-left at the origin using tempPaint.
+    SkMatrix translate;
+    translate.setTranslate(-maskRect.fLeft, -maskRect.fTop);
+    drawContext->drawPath(mask->asRenderTarget(), clip, tempPaint, translate, devPath, strokeInfo);
+    return mask;
+}
+
+void GrBlurUtils::drawPathWithMaskFilter(GrContext* context, 
+                                         GrDrawContext* drawContext,
+                                         GrRenderTarget* renderTarget,
+                                         const GrClip& clip,
+                                         const SkPath& origSrcPath,
+                                         const SkPaint& paint,
+                                         const SkMatrix& origViewMatrix,
+                                         const SkMatrix* prePathMatrix,
+                                         const SkIRect& clipBounds,
+                                         bool pathIsMutable) {
+    SkASSERT(!pathIsMutable || origSrcPath.isVolatile());
+
+    GrStrokeInfo strokeInfo(paint);
+
+    // If we have a prematrix, apply it to the path, optimizing for the case
+    // where the original path can in fact be modified in place (even though
+    // its parameter type is const).
+    SkPath* pathPtr = const_cast<SkPath*>(&origSrcPath);
+    SkTLazy<SkPath> tmpPath;
+    SkTLazy<SkPath> effectPath;
+    SkPathEffect* pathEffect = paint.getPathEffect();
+
+    SkMatrix viewMatrix = origViewMatrix;
+
+    if (prePathMatrix) {
+        // stroking, path effects, and blurs are supposed to be applied *after* the prePathMatrix.
+        // The pre-path-matrix also should not affect shading.
+        if (NULL == paint.getMaskFilter() && NULL == pathEffect && NULL == paint.getShader() &&
+            (strokeInfo.isFillStyle() || strokeInfo.isHairlineStyle())) {
+            viewMatrix.preConcat(*prePathMatrix);
+        } else {
+            SkPath* result = pathPtr;
+
+            if (!pathIsMutable) {
+                result = tmpPath.init();
+                result->setIsVolatile(true);
+                pathIsMutable = true;
+            }
+            // should I push prePathMatrix on our MV stack temporarily, instead
+            // of applying it here? See SkDraw.cpp
+            pathPtr->transform(*prePathMatrix, result);
+            pathPtr = result;
+        }
+    }
+    // at this point we're done with prePathMatrix
+    SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
+
+    GrPaint grPaint;
+    if (!SkPaint2GrPaint(context, renderTarget, paint, viewMatrix, true, &grPaint)) {
+        return;
+    }
+
+    const SkRect* cullRect = NULL;  // TODO: what is our bounds?
+    if (!strokeInfo.isDashed() && pathEffect && pathEffect->filterPath(effectPath.init(), *pathPtr,
+                                                                       &strokeInfo, cullRect)) {
+        pathPtr = effectPath.get();
+        pathIsMutable = true;
+    }
+
+    if (paint.getMaskFilter()) {
+        if (!strokeInfo.isHairlineStyle()) {
+            SkPath* strokedPath = pathIsMutable ? pathPtr : tmpPath.init();
+            if (strokeInfo.isDashed()) {
+                if (pathEffect->filterPath(strokedPath, *pathPtr, &strokeInfo, cullRect)) {
+                    pathPtr = strokedPath;
+                    pathIsMutable = true;
+                }
+                strokeInfo.removeDash();
+            }
+            if (strokeInfo.applyToPath(strokedPath, *pathPtr)) {
+                pathPtr = strokedPath;
+                pathIsMutable = true;
+                strokeInfo.setFillStyle();
+            }
+        }
+
+        // avoid possibly allocating a new path in transform if we can
+        SkPath* devPathPtr = pathIsMutable ? pathPtr : tmpPath.init();
+        if (!pathIsMutable) {
+            devPathPtr->setIsVolatile(true);
+        }
+
+        // transform the path into device space
+        pathPtr->transform(viewMatrix, devPathPtr);
+
+        SkRect maskRect;
+        if (paint.getMaskFilter()->canFilterMaskGPU(devPathPtr->getBounds(),
+                                                    clipBounds,
+                                                    viewMatrix,
+                                                    &maskRect)) {
+            SkIRect finalIRect;
+            maskRect.roundOut(&finalIRect);
+            if (clip_bounds_quick_reject(clipBounds, finalIRect)) {
+                // clipped out
+                return;
+            }
+
+            if (paint.getMaskFilter()->directFilterMaskGPU(context,
+                                                           renderTarget,
+                                                           &grPaint,
+                                                           clip,
+                                                           viewMatrix,
+                                                           strokeInfo,
+                                                           *devPathPtr)) {
+                // the mask filter was able to draw itself directly, so there's nothing
+                // left to do.
+                return;
+            }
+
+
+            SkAutoTUnref<GrTexture> mask(create_mask_GPU(context,
+                                                         maskRect,
+                                                         *devPathPtr,
+                                                         strokeInfo,
+                                                         grPaint.isAntiAlias(),
+                                                         renderTarget->numSamples()));
+            if (mask) {
+                GrTexture* filtered;
+
+                if (paint.getMaskFilter()->filterMaskGPU(mask, viewMatrix, maskRect,
+                                                         &filtered, true)) {
+                    // filterMaskGPU gives us ownership of a ref to the result
+                    SkAutoTUnref<GrTexture> atu(filtered);
+                    if (draw_mask(drawContext,
+                                  renderTarget,
+                                  clip,
+                                  viewMatrix,
+                                  maskRect,
+                                  &grPaint,
+                                  filtered)) {
+                        // This path is completely drawn
+                        return;
+                    }
+                }
+            }
+        }
+
+        // draw the mask on the CPU - this is a fallthrough path in case the
+        // GPU path fails
+        SkPaint::Style style = strokeInfo.isHairlineStyle() ? SkPaint::kStroke_Style :
+                                                              SkPaint::kFill_Style;
+        draw_with_mask_filter(drawContext, context->textureProvider(), renderTarget,
+                              clip, viewMatrix, *devPathPtr,
+                              paint.getMaskFilter(), clipBounds, &grPaint, style);
+        return;
+    }
+
+    drawContext->drawPath(renderTarget, clip, grPaint, viewMatrix, *pathPtr, strokeInfo);
+}
+