2015-05-27 18:02:55 +00:00
|
|
|
/*
|
|
|
|
* 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"
|
2015-05-29 15:02:10 +00:00
|
|
|
#include "GrCaps.h"
|
2015-05-27 18:02:55 +00:00
|
|
|
#include "GrContext.h"
|
|
|
|
#include "effects/GrSimpleTextureEffect.h"
|
|
|
|
#include "GrStrokeInfo.h"
|
|
|
|
#include "GrTexture.h"
|
|
|
|
#include "GrTextureProvider.h"
|
|
|
|
#include "SkDraw.h"
|
2015-09-28 13:26:28 +00:00
|
|
|
#include "SkGrPriv.h"
|
2015-05-27 18:02:55 +00:00
|
|
|
#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,
|
|
|
|
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());
|
|
|
|
|
2015-10-06 15:40:50 +00:00
|
|
|
grp->addCoverageFragmentProcessor(GrSimpleTextureEffect::Create(mask, matrix,
|
|
|
|
kDevice_GrCoordSet))->unref();
|
2015-05-27 18:02:55 +00:00
|
|
|
|
|
|
|
SkMatrix inverse;
|
|
|
|
if (!viewMatrix.invert(&inverse)) {
|
|
|
|
return false;
|
|
|
|
}
|
2015-11-05 18:41:43 +00:00
|
|
|
drawContext->fillRectWithLocalMatrix(clip, *grp, SkMatrix::I(), maskRect, inverse);
|
2015-05-27 18:02:55 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-11-30 13:45:06 +00:00
|
|
|
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,
|
|
|
|
SkPaint::Style style) {
|
2015-05-27 18:02:55 +00:00
|
|
|
SkMask srcM, dstM;
|
|
|
|
|
|
|
|
if (!SkDraw::DrawToMask(devPath, &clipBounds, filter, &viewMatrix, &srcM,
|
|
|
|
SkMask::kComputeBoundsAndRenderImage_CreateMode, style)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
SkAutoMaskFreeImage autoSrc(srcM.fImage);
|
|
|
|
|
2015-08-27 14:41:13 +00:00
|
|
|
if (!filter->filterMask(&dstM, srcM, viewMatrix, nullptr)) {
|
2015-05-27 18:02:55 +00:00
|
|
|
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;
|
|
|
|
|
2015-07-31 20:59:30 +00:00
|
|
|
SkAutoTUnref<GrTexture> texture(textureProvider->createApproxTexture(desc));
|
2015-05-27 18:02:55 +00:00
|
|
|
if (!texture) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
|
|
|
|
dstM.fImage, dstM.fRowBytes);
|
|
|
|
|
|
|
|
SkRect maskRect = SkRect::Make(dstM.fBounds);
|
|
|
|
|
2015-10-15 15:01:48 +00:00
|
|
|
return draw_mask(drawContext, clipData, viewMatrix, maskRect, grp, texture);
|
2015-05-27 18:02:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Create a mask of 'devPath' and place the result in 'mask'.
|
2016-04-28 13:21:55 +00:00
|
|
|
static GrTexture* create_mask_GPU(GrContext* context,
|
|
|
|
SkRect* maskRect,
|
|
|
|
const SkPath& devPath,
|
|
|
|
const GrStrokeInfo& strokeInfo,
|
|
|
|
bool doAA,
|
|
|
|
int sampleCnt) {
|
2016-03-29 16:03:52 +00:00
|
|
|
// 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
|
2015-09-14 18:18:13 +00:00
|
|
|
// so the mask draws in a reproducible manner.
|
|
|
|
*maskRect = SkRect::Make(maskRect->roundOut());
|
|
|
|
|
2016-04-28 13:21:55 +00:00
|
|
|
GrSurfaceDesc desc;
|
|
|
|
desc.fFlags = kRenderTarget_GrSurfaceFlag;
|
|
|
|
desc.fWidth = SkScalarCeilToInt(maskRect->width());
|
|
|
|
desc.fHeight = SkScalarCeilToInt(maskRect->height());
|
|
|
|
desc.fSampleCnt = doAA ? sampleCnt : 0;
|
2016-04-27 20:34:01 +00:00
|
|
|
// We actually only need A8, but it often isn't supported as a
|
|
|
|
// render target so default to RGBA_8888
|
2016-04-28 13:21:55 +00:00
|
|
|
desc.fConfig = kRGBA_8888_GrPixelConfig;
|
|
|
|
|
|
|
|
if (context->caps()->isConfigRenderable(kAlpha_8_GrPixelConfig, desc.fSampleCnt > 0)) {
|
|
|
|
desc.fConfig = kAlpha_8_GrPixelConfig;
|
2015-05-27 18:02:55 +00:00
|
|
|
}
|
|
|
|
|
2016-04-28 13:21:55 +00:00
|
|
|
GrTexture* mask = context->textureProvider()->createApproxTexture(desc);
|
|
|
|
if (nullptr == mask) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
SkRect clipRect = SkRect::MakeWH(maskRect->width(), maskRect->height());
|
|
|
|
|
|
|
|
sk_sp<GrDrawContext> drawContext(context->drawContext(sk_ref_sp(mask->asRenderTarget())));
|
2015-05-27 18:02:55 +00:00
|
|
|
if (!drawContext) {
|
2015-08-27 14:41:13 +00:00
|
|
|
return nullptr;
|
2015-05-27 18:02:55 +00:00
|
|
|
}
|
|
|
|
|
2015-10-15 15:01:48 +00:00
|
|
|
drawContext->clear(nullptr, 0x0, true);
|
2015-05-27 18:02:55 +00:00
|
|
|
|
|
|
|
GrPaint tempPaint;
|
|
|
|
tempPaint.setAntiAlias(doAA);
|
|
|
|
tempPaint.setCoverageSetOpXPFactory(SkRegion::kReplace_Op);
|
|
|
|
|
|
|
|
// setup new clip
|
|
|
|
GrClip clip(clipRect);
|
|
|
|
|
2015-09-14 18:18:13 +00:00
|
|
|
// Draw the mask into maskTexture with the path's integerized top-left at
|
|
|
|
// the origin using tempPaint.
|
2015-05-27 18:02:55 +00:00
|
|
|
SkMatrix translate;
|
2015-09-14 18:18:13 +00:00
|
|
|
translate.setTranslate(-maskRect->fLeft, -maskRect->fTop);
|
2015-10-15 15:01:48 +00:00
|
|
|
drawContext->drawPath(clip, tempPaint, translate, devPath, strokeInfo);
|
2016-04-28 13:21:55 +00:00
|
|
|
return mask;
|
2015-05-27 18:02:55 +00:00
|
|
|
}
|
|
|
|
|
2015-11-09 19:55:57 +00:00
|
|
|
static void draw_path_with_mask_filter(GrContext* context,
|
|
|
|
GrDrawContext* drawContext,
|
|
|
|
const GrClip& clip,
|
|
|
|
GrPaint* paint,
|
|
|
|
const SkMatrix& viewMatrix,
|
|
|
|
const SkMaskFilter* maskFilter,
|
|
|
|
const SkPathEffect* pathEffect,
|
|
|
|
const GrStrokeInfo& origStrokeInfo,
|
|
|
|
SkPath* pathPtr,
|
|
|
|
bool pathIsMutable) {
|
|
|
|
SkASSERT(maskFilter);
|
|
|
|
|
|
|
|
SkIRect clipBounds;
|
2015-12-01 20:51:26 +00:00
|
|
|
clip.getConservativeBounds(drawContext->width(), drawContext->height(), &clipBounds);
|
2015-11-09 19:55:57 +00:00
|
|
|
SkTLazy<SkPath> tmpPath;
|
|
|
|
GrStrokeInfo strokeInfo(origStrokeInfo);
|
|
|
|
|
|
|
|
static const SkRect* cullRect = nullptr; // TODO: what is our bounds?
|
|
|
|
|
2015-11-30 13:45:06 +00:00
|
|
|
SkASSERT(strokeInfo.isDashed() || !pathEffect);
|
|
|
|
|
2015-11-09 19:55:57 +00:00
|
|
|
if (!strokeInfo.isHairlineStyle()) {
|
|
|
|
SkPath* strokedPath = pathIsMutable ? pathPtr : tmpPath.init();
|
|
|
|
if (strokeInfo.isDashed()) {
|
|
|
|
if (pathEffect->filterPath(strokedPath, *pathPtr, &strokeInfo, cullRect)) {
|
|
|
|
pathPtr = strokedPath;
|
|
|
|
pathPtr->setIsVolatile(true);
|
|
|
|
pathIsMutable = true;
|
|
|
|
}
|
|
|
|
strokeInfo.removeDash();
|
|
|
|
}
|
|
|
|
if (strokeInfo.applyToPath(strokedPath, *pathPtr)) {
|
2015-11-30 13:45:06 +00:00
|
|
|
// Apply the stroke to the path if there is one
|
2015-11-09 19:55:57 +00:00
|
|
|
pathPtr = strokedPath;
|
|
|
|
pathPtr->setIsVolatile(true);
|
|
|
|
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 (maskFilter->canFilterMaskGPU(SkRRect::MakeRect(devPathPtr->getBounds()),
|
|
|
|
clipBounds,
|
|
|
|
viewMatrix,
|
|
|
|
&maskRect)) {
|
|
|
|
SkIRect finalIRect;
|
|
|
|
maskRect.roundOut(&finalIRect);
|
|
|
|
if (clip_bounds_quick_reject(clipBounds, finalIRect)) {
|
|
|
|
// clipped out
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (maskFilter->directFilterMaskGPU(context->textureProvider(),
|
|
|
|
drawContext,
|
|
|
|
paint,
|
|
|
|
clip,
|
|
|
|
viewMatrix,
|
|
|
|
strokeInfo,
|
|
|
|
*devPathPtr)) {
|
|
|
|
// the mask filter was able to draw itself directly, so there's nothing
|
|
|
|
// left to do.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-04-28 13:21:55 +00:00
|
|
|
SkAutoTUnref<GrTexture> mask(create_mask_GPU(context,
|
|
|
|
&maskRect,
|
|
|
|
*devPathPtr,
|
|
|
|
strokeInfo,
|
|
|
|
paint->isAntiAlias(),
|
|
|
|
drawContext->numColorSamples()));
|
2015-11-09 19:55:57 +00:00
|
|
|
if (mask) {
|
|
|
|
GrTexture* filtered;
|
|
|
|
|
2016-04-28 13:21:55 +00:00
|
|
|
if (maskFilter->filterMaskGPU(mask, viewMatrix, maskRect, &filtered, true)) {
|
2015-11-09 19:55:57 +00:00
|
|
|
// filterMaskGPU gives us ownership of a ref to the result
|
|
|
|
SkAutoTUnref<GrTexture> atu(filtered);
|
|
|
|
if (draw_mask(drawContext, clip, viewMatrix, maskRect, paint, 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;
|
2015-11-30 13:45:06 +00:00
|
|
|
sw_draw_with_mask_filter(drawContext, context->textureProvider(),
|
|
|
|
clip, viewMatrix, *devPathPtr,
|
|
|
|
maskFilter, clipBounds, paint, style);
|
2015-11-09 19:55:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void GrBlurUtils::drawPathWithMaskFilter(GrContext* context,
|
|
|
|
GrDrawContext* drawContext,
|
|
|
|
const GrClip& clip,
|
|
|
|
const SkPath& origPath,
|
|
|
|
GrPaint* paint,
|
|
|
|
const SkMatrix& viewMatrix,
|
|
|
|
const SkMaskFilter* mf,
|
2015-11-30 13:45:06 +00:00
|
|
|
const SkPathEffect* pathEffect,
|
|
|
|
const GrStrokeInfo& origStrokeInfo,
|
|
|
|
bool pathIsMutable) {
|
|
|
|
SkPath* pathPtr = const_cast<SkPath*>(&origPath);
|
|
|
|
|
|
|
|
SkTLazy<SkPath> tmpPath;
|
|
|
|
GrStrokeInfo strokeInfo(origStrokeInfo);
|
|
|
|
|
|
|
|
if (!strokeInfo.isDashed() && pathEffect && pathEffect->filterPath(tmpPath.init(), *pathPtr,
|
|
|
|
&strokeInfo, nullptr)) {
|
|
|
|
pathPtr = tmpPath.get();
|
|
|
|
pathPtr->setIsVolatile(true);
|
|
|
|
pathIsMutable = true;
|
|
|
|
pathEffect = nullptr;
|
|
|
|
}
|
|
|
|
|
2015-12-01 20:51:26 +00:00
|
|
|
draw_path_with_mask_filter(context, drawContext, clip, paint, viewMatrix, mf, pathEffect,
|
2015-11-30 13:45:06 +00:00
|
|
|
strokeInfo, pathPtr, pathIsMutable);
|
2015-11-09 19:55:57 +00:00
|
|
|
}
|
|
|
|
|
2016-03-29 16:03:52 +00:00
|
|
|
void GrBlurUtils::drawPathWithMaskFilter(GrContext* context,
|
2015-05-27 18:02:55 +00:00
|
|
|
GrDrawContext* drawContext,
|
|
|
|
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);
|
2016-02-04 19:35:06 +00:00
|
|
|
// comment out the line below to determine if it is the reason that the chrome mac perf bot
|
|
|
|
// has begun crashing
|
|
|
|
// strokeInfo.setResScale(SkDraw::ComputeResScaleForStroking(origViewMatrix));
|
2015-05-27 18:02:55 +00:00
|
|
|
|
|
|
|
// 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.
|
2015-11-09 19:55:57 +00:00
|
|
|
if (!paint.getMaskFilter() && !pathEffect && !paint.getShader() &&
|
2015-05-27 18:02:55 +00:00
|
|
|
(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;)
|
|
|
|
|
2015-11-30 13:45:06 +00:00
|
|
|
SkTLazy<SkPath> tmpPath2;
|
|
|
|
|
|
|
|
if (!strokeInfo.isDashed() && pathEffect &&
|
|
|
|
pathEffect->filterPath(tmpPath2.init(), *pathPtr, &strokeInfo, nullptr)) {
|
|
|
|
pathPtr = tmpPath2.get();
|
|
|
|
pathPtr->setIsVolatile(true);
|
|
|
|
pathIsMutable = true;
|
|
|
|
pathEffect = nullptr;
|
|
|
|
}
|
|
|
|
|
2015-05-27 18:02:55 +00:00
|
|
|
GrPaint grPaint;
|
2016-04-13 20:10:14 +00:00
|
|
|
if (!SkPaintToGrPaint(context, paint, viewMatrix, drawContext->isGammaCorrect(),
|
2016-04-06 14:38:23 +00:00
|
|
|
&grPaint)) {
|
2015-05-27 18:02:55 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (paint.getMaskFilter()) {
|
2015-12-01 20:51:26 +00:00
|
|
|
draw_path_with_mask_filter(context, drawContext, clip, &grPaint, viewMatrix,
|
2015-11-30 13:45:06 +00:00
|
|
|
paint.getMaskFilter(), pathEffect, strokeInfo,
|
2015-11-09 19:55:57 +00:00
|
|
|
pathPtr, pathIsMutable);
|
|
|
|
} else {
|
|
|
|
drawContext->drawPath(clip, grPaint, viewMatrix, *pathPtr, strokeInfo);
|
2015-05-27 18:02:55 +00:00
|
|
|
}
|
|
|
|
}
|