summaryrefslogtreecommitdiffstats
path: root/gfx/skia/skia/src/gpu/GrBlurUtils.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/skia/skia/src/gpu/GrBlurUtils.cpp')
-rw-r--r--gfx/skia/skia/src/gpu/GrBlurUtils.cpp295
1 files changed, 295 insertions, 0 deletions
diff --git a/gfx/skia/skia/src/gpu/GrBlurUtils.cpp b/gfx/skia/skia/src/gpu/GrBlurUtils.cpp
new file mode 100644
index 000000000..5f575e3c8
--- /dev/null
+++ b/gfx/skia/skia/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 "GrCaps.h"
+#include "GrContext.h"
+#include "GrFixedClip.h"
+#include "effects/GrSimpleTextureEffect.h"
+#include "GrStyle.h"
+#include "GrTexture.h"
+#include "GrTextureProvider.h"
+#include "SkDraw.h"
+#include "SkGrPriv.h"
+#include "SkMaskFilter.h"
+#include "SkPaint.h"
+#include "SkTLazy.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,
+ const GrClip& clip,
+ const SkMatrix& viewMatrix,
+ const SkIRect& maskRect,
+ GrPaint* grp,
+ GrTexture* mask) {
+ SkMatrix matrix;
+ matrix.setTranslate(-SkIntToScalar(maskRect.fLeft), -SkIntToScalar(maskRect.fTop));
+ matrix.postIDiv(mask->width(), mask->height());
+ matrix.preConcat(viewMatrix);
+ grp->addCoverageFragmentProcessor(GrSimpleTextureEffect::Make(mask, nullptr, matrix));
+
+ SkMatrix inverse;
+ if (!viewMatrix.invert(&inverse)) {
+ return false;
+ }
+ drawContext->fillRectWithLocalMatrix(clip, *grp, SkMatrix::I(), SkRect::Make(maskRect),
+ inverse);
+ return true;
+}
+
+static bool sw_draw_with_mask_filter(GrDrawContext* drawContext,
+ GrTextureProvider* textureProvider,
+ const GrClip& clipData,
+ const SkMatrix& viewMatrix,
+ const SkPath& devPath,
+ const SkMaskFilter* filter,
+ const SkIRect& clipBounds,
+ GrPaint* grp,
+ SkStrokeRec::InitStyle fillOrHairline) {
+ SkMask srcM, dstM;
+ if (!SkDraw::DrawToMask(devPath, &clipBounds, filter, &viewMatrix, &srcM,
+ SkMask::kComputeBoundsAndRenderImage_CreateMode, fillOrHairline)) {
+ return false;
+ }
+ SkAutoMaskFreeImage autoSrc(srcM.fImage);
+
+ if (!filter->filterMask(&dstM, srcM, viewMatrix, nullptr)) {
+ 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->createApproxTexture(desc));
+ if (!texture) {
+ return false;
+ }
+ texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
+ dstM.fImage, dstM.fRowBytes);
+
+ return draw_mask(drawContext, clipData, viewMatrix, dstM.fBounds, grp, texture);
+}
+
+// Create a mask of 'devPath' and place the result in 'mask'.
+static sk_sp<GrTexture> create_mask_GPU(GrContext* context,
+ const SkIRect& maskRect,
+ const SkPath& devPath,
+ SkStrokeRec::InitStyle fillOrHairline,
+ bool doAA,
+ int sampleCnt) {
+ if (!doAA) {
+ // Don't need MSAA if mask isn't AA
+ sampleCnt = 0;
+ }
+
+ sk_sp<GrDrawContext> drawContext(context->makeDrawContextWithFallback(SkBackingFit::kApprox,
+ maskRect.width(),
+ maskRect.height(),
+ kAlpha_8_GrPixelConfig,
+ nullptr,
+ sampleCnt));
+ if (!drawContext) {
+ return nullptr;
+ }
+
+ drawContext->clear(nullptr, 0x0, true);
+
+ GrPaint tempPaint;
+ tempPaint.setAntiAlias(doAA);
+ tempPaint.setCoverageSetOpXPFactory(SkRegion::kReplace_Op);
+
+ // setup new clip
+ const SkIRect clipRect = SkIRect::MakeWH(maskRect.width(), maskRect.height());
+ GrFixedClip clip(clipRect);
+
+ // Draw the mask into maskTexture with the path's integerized top-left at
+ // the origin using tempPaint.
+ SkMatrix translate;
+ translate.setTranslate(-SkIntToScalar(maskRect.fLeft), -SkIntToScalar(maskRect.fTop));
+ drawContext->drawPath(clip, tempPaint, translate, devPath, GrStyle(fillOrHairline));
+ return drawContext->asTexture();;
+}
+
+static void draw_path_with_mask_filter(GrContext* context,
+ GrDrawContext* drawContext,
+ const GrClip& clip,
+ GrPaint* paint,
+ const SkMatrix& viewMatrix,
+ const SkMaskFilter* maskFilter,
+ const GrStyle& style,
+ const SkPath* path,
+ bool pathIsMutable) {
+ SkASSERT(maskFilter);
+
+ SkIRect clipBounds;
+ clip.getConservativeBounds(drawContext->width(), drawContext->height(), &clipBounds);
+ SkTLazy<SkPath> tmpPath;
+ SkStrokeRec::InitStyle fillOrHairline;
+
+ // We just fully apply the style here.
+ if (style.applies()) {
+ SkScalar scale = GrStyle::MatrixToScaleFactor(viewMatrix);
+ if (0 == scale || !style.applyToPath(tmpPath.init(), &fillOrHairline, *path, scale)) {
+ return;
+ }
+ pathIsMutable = true;
+ path = tmpPath.get();
+ } else if (style.isSimpleHairline()) {
+ fillOrHairline = SkStrokeRec::kHairline_InitStyle;
+ } else {
+ SkASSERT(style.isSimpleFill());
+ fillOrHairline = SkStrokeRec::kFill_InitStyle;
+ }
+
+ // transform the path into device space
+ if (!viewMatrix.isIdentity()) {
+ SkPath* result;
+ if (pathIsMutable) {
+ result = const_cast<SkPath*>(path);
+ } else {
+ if (!tmpPath.isValid()) {
+ tmpPath.init();
+ }
+ result = tmpPath.get();
+ }
+ path->transform(viewMatrix, result);
+ path = result;
+ result->setIsVolatile(true);
+ pathIsMutable = true;
+ }
+
+ SkRect maskRect;
+ if (maskFilter->canFilterMaskGPU(SkRRect::MakeRect(path->getBounds()),
+ clipBounds,
+ viewMatrix,
+ &maskRect)) {
+ // This mask will ultimately be drawn as a non-AA rect (see draw_mask).
+ // Non-AA rects have a bad habit of snapping arbitrarily. Integerize here
+ // so the mask draws in a reproducible manner.
+ SkIRect finalIRect;
+ maskRect.roundOut(&finalIRect);
+ if (clip_bounds_quick_reject(clipBounds, finalIRect)) {
+ // clipped out
+ return;
+ }
+
+ if (maskFilter->directFilterMaskGPU(context->textureProvider(),
+ drawContext,
+ paint,
+ clip,
+ viewMatrix,
+ SkStrokeRec(fillOrHairline),
+ *path)) {
+ // the mask filter was able to draw itself directly, so there's nothing
+ // left to do.
+ return;
+ }
+
+ sk_sp<GrTexture> mask(create_mask_GPU(context,
+ finalIRect,
+ *path,
+ fillOrHairline,
+ paint->isAntiAlias(),
+ drawContext->numColorSamples()));
+ if (mask) {
+ GrTexture* filtered;
+
+ if (maskFilter->filterMaskGPU(mask.get(), viewMatrix, finalIRect, &filtered)) {
+ // filterMaskGPU gives us ownership of a ref to the result
+ SkAutoTUnref<GrTexture> atu(filtered);
+ if (draw_mask(drawContext, clip, viewMatrix, finalIRect, paint, filtered)) {
+ // This path is completely drawn
+ return;
+ }
+ }
+ }
+ }
+
+ sw_draw_with_mask_filter(drawContext, context->textureProvider(),
+ clip, viewMatrix, *path,
+ maskFilter, clipBounds, paint, fillOrHairline);
+}
+
+void GrBlurUtils::drawPathWithMaskFilter(GrContext* context,
+ GrDrawContext* drawContext,
+ const GrClip& clip,
+ const SkPath& path,
+ GrPaint* paint,
+ const SkMatrix& viewMatrix,
+ const SkMaskFilter* mf,
+ const GrStyle& style,
+ bool pathIsMutable) {
+ draw_path_with_mask_filter(context, drawContext, clip, paint, viewMatrix, mf,
+ style, &path, pathIsMutable);
+}
+
+void GrBlurUtils::drawPathWithMaskFilter(GrContext* context,
+ GrDrawContext* drawContext,
+ const GrClip& clip,
+ const SkPath& origPath,
+ const SkPaint& paint,
+ const SkMatrix& origViewMatrix,
+ const SkMatrix* prePathMatrix,
+ const SkIRect& clipBounds,
+ bool pathIsMutable) {
+ SkASSERT(!pathIsMutable || origPath.isVolatile());
+
+ GrStyle style(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).
+
+ const SkPath* path = &origPath;
+ SkTLazy<SkPath> tmpPath;
+
+ SkMatrix viewMatrix = origViewMatrix;
+
+ if (prePathMatrix) {
+ // Styling, blurs, and shading are supposed to be applied *after* the prePathMatrix.
+ if (!paint.getMaskFilter() && !paint.getShader() && !style.applies()) {
+ viewMatrix.preConcat(*prePathMatrix);
+ } else {
+ SkPath* result = pathIsMutable ? const_cast<SkPath*>(path) : tmpPath.init();
+ pathIsMutable = true;
+ path->transform(*prePathMatrix, result);
+ path = result;
+ result->setIsVolatile(true);
+ }
+ }
+ // at this point we're done with prePathMatrix
+ SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
+
+ GrPaint grPaint;
+ if (!SkPaintToGrPaint(context, drawContext, paint, viewMatrix, &grPaint)) {
+ return;
+ }
+
+ if (paint.getMaskFilter()) {
+ draw_path_with_mask_filter(context, drawContext, clip, &grPaint, viewMatrix,
+ paint.getMaskFilter(), style,
+ path, pathIsMutable);
+ } else {
+ drawContext->drawPath(clip, grPaint, viewMatrix, *path, style);
+ }
+}