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);
+}
+