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"
|
2018-03-08 14:51:04 +00:00
|
|
|
|
2015-05-29 15:02:10 +00:00
|
|
|
#include "GrCaps.h"
|
2015-05-27 18:02:55 +00:00
|
|
|
#include "GrContext.h"
|
2017-01-19 21:59:04 +00:00
|
|
|
#include "GrContextPriv.h"
|
2016-08-19 20:29:27 +00:00
|
|
|
#include "GrFixedClip.h"
|
2018-03-08 14:51:04 +00:00
|
|
|
#include "GrProxyProvider.h"
|
|
|
|
#include "GrRenderTargetContext.h"
|
2016-12-09 18:35:02 +00:00
|
|
|
#include "GrRenderTargetContextPriv.h"
|
2016-05-10 16:14:17 +00:00
|
|
|
#include "GrStyle.h"
|
2016-11-21 19:05:03 +00:00
|
|
|
#include "GrTextureProxy.h"
|
2018-03-08 14:51:04 +00:00
|
|
|
#include "effects/GrSimpleTextureEffect.h"
|
|
|
|
|
2015-05-27 18:02:55 +00:00
|
|
|
#include "SkDraw.h"
|
2017-03-07 21:58:08 +00:00
|
|
|
#include "SkGr.h"
|
2018-01-23 20:29:32 +00:00
|
|
|
#include "SkMaskFilterBase.h"
|
2015-05-27 18:02:55 +00:00
|
|
|
#include "SkPaint.h"
|
2016-08-06 05:32:12 +00:00
|
|
|
#include "SkTLazy.h"
|
2015-05-27 18:02:55 +00:00
|
|
|
|
|
|
|
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.
|
2017-03-15 14:42:12 +00:00
|
|
|
static bool draw_mask(GrRenderTargetContext* renderTargetContext,
|
2015-05-27 18:02:55 +00:00
|
|
|
const GrClip& clip,
|
|
|
|
const SkMatrix& viewMatrix,
|
2016-05-13 12:06:19 +00:00
|
|
|
const SkIRect& maskRect,
|
2017-01-11 18:42:54 +00:00
|
|
|
GrPaint&& paint,
|
2016-12-14 14:00:07 +00:00
|
|
|
sk_sp<GrTextureProxy> mask) {
|
2017-01-11 18:42:54 +00:00
|
|
|
SkMatrix inverse;
|
|
|
|
if (!viewMatrix.invert(&inverse)) {
|
|
|
|
return false;
|
|
|
|
}
|
2016-12-14 14:00:07 +00:00
|
|
|
|
2017-01-20 17:44:06 +00:00
|
|
|
SkMatrix matrix = SkMatrix::MakeTrans(-SkIntToScalar(maskRect.fLeft),
|
|
|
|
-SkIntToScalar(maskRect.fTop));
|
2016-10-03 21:15:28 +00:00
|
|
|
matrix.preConcat(viewMatrix);
|
2017-10-18 17:15:13 +00:00
|
|
|
paint.addCoverageFragmentProcessor(GrSimpleTextureEffect::Make(std::move(mask), matrix));
|
2015-05-27 18:02:55 +00:00
|
|
|
|
2017-01-11 18:42:54 +00:00
|
|
|
renderTargetContext->fillRectWithLocalMatrix(clip, std::move(paint), GrAA::kNo, SkMatrix::I(),
|
2016-12-09 20:10:07 +00:00
|
|
|
SkRect::Make(maskRect), inverse);
|
2015-05-27 18:02:55 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-03-08 14:51:04 +00:00
|
|
|
static void mask_release_proc(void* addr, void* /*context*/) {
|
|
|
|
SkMask::FreeImage(addr);
|
|
|
|
}
|
|
|
|
|
2016-12-14 14:00:07 +00:00
|
|
|
static bool sw_draw_with_mask_filter(GrContext* context,
|
|
|
|
GrRenderTargetContext* renderTargetContext,
|
2015-11-30 13:45:06 +00:00
|
|
|
const GrClip& clipData,
|
|
|
|
const SkMatrix& viewMatrix,
|
|
|
|
const SkPath& devPath,
|
|
|
|
const SkMaskFilter* filter,
|
|
|
|
const SkIRect& clipBounds,
|
2017-01-11 18:42:54 +00:00
|
|
|
GrPaint&& paint,
|
2016-05-10 16:14:17 +00:00
|
|
|
SkStrokeRec::InitStyle fillOrHairline) {
|
2018-03-08 14:51:04 +00:00
|
|
|
SkMask srcM, dstM;
|
2015-05-27 18:02:55 +00:00
|
|
|
if (!SkDraw::DrawToMask(devPath, &clipBounds, filter, &viewMatrix, &srcM,
|
2016-05-10 16:14:17 +00:00
|
|
|
SkMask::kComputeBoundsAndRenderImage_CreateMode, fillOrHairline)) {
|
2015-05-27 18:02:55 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
SkAutoMaskFreeImage autoSrc(srcM.fImage);
|
|
|
|
|
2018-01-23 20:29:32 +00:00
|
|
|
if (!as_MFB(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
|
2018-03-08 14:51:04 +00:00
|
|
|
SkBitmap bm;
|
|
|
|
if (!bm.installPixels(SkImageInfo::MakeA8(dstM.fBounds.width(), dstM.fBounds.height()),
|
|
|
|
autoDst.release(), dstM.fRowBytes, mask_release_proc, nullptr)) {
|
2016-12-14 14:00:07 +00:00
|
|
|
return false;
|
|
|
|
}
|
2018-03-08 14:51:04 +00:00
|
|
|
bm.setImmutable();
|
2016-12-14 14:00:07 +00:00
|
|
|
|
2018-03-08 14:51:04 +00:00
|
|
|
sk_sp<SkImage> image = SkImage::MakeFromBitmap(bm);
|
2018-03-23 14:33:12 +00:00
|
|
|
if (!image) {
|
|
|
|
return false;
|
|
|
|
}
|
2018-03-08 14:51:04 +00:00
|
|
|
|
|
|
|
auto proxyProvider = context->contextPriv().proxyProvider();
|
|
|
|
sk_sp<GrTextureProxy> maskProxy = proxyProvider->createTextureProxy(std::move(image),
|
|
|
|
kNone_GrSurfaceFlags,
|
|
|
|
1, SkBudgeted::kYes,
|
|
|
|
SkBackingFit::kApprox);
|
2018-03-23 14:33:12 +00:00
|
|
|
if (!maskProxy) {
|
|
|
|
return false;
|
|
|
|
}
|
2016-12-14 14:00:07 +00:00
|
|
|
|
2017-03-15 14:42:12 +00:00
|
|
|
return draw_mask(renderTargetContext, clipData, viewMatrix,
|
2018-03-08 14:51:04 +00:00
|
|
|
dstM.fBounds, std::move(paint), std::move(maskProxy));
|
2015-05-27 18:02:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Create a mask of 'devPath' and place the result in 'mask'.
|
2016-11-21 19:05:03 +00:00
|
|
|
static sk_sp<GrTextureProxy> create_mask_GPU(GrContext* context,
|
|
|
|
const SkIRect& maskRect,
|
|
|
|
const SkPath& devPath,
|
|
|
|
SkStrokeRec::InitStyle fillOrHairline,
|
2016-12-09 20:10:07 +00:00
|
|
|
GrAA aa,
|
2016-11-21 19:05:03 +00:00
|
|
|
int sampleCnt) {
|
2016-12-09 20:10:07 +00:00
|
|
|
if (GrAA::kNo == aa) {
|
2016-04-28 16:55:15 +00:00
|
|
|
// Don't need MSAA if mask isn't AA
|
2018-02-03 01:32:49 +00:00
|
|
|
sampleCnt = 1;
|
2015-05-27 18:02:55 +00:00
|
|
|
}
|
|
|
|
|
2018-03-06 13:20:37 +00:00
|
|
|
sk_sp<GrRenderTargetContext> rtContext(
|
|
|
|
context->contextPriv().makeDeferredRenderTargetContextWithFallback(
|
|
|
|
SkBackingFit::kApprox, maskRect.width(), maskRect.height(), kAlpha_8_GrPixelConfig,
|
|
|
|
nullptr, sampleCnt));
|
2016-11-21 19:05:03 +00:00
|
|
|
if (!rtContext) {
|
2015-08-27 14:41:13 +00:00
|
|
|
return nullptr;
|
2015-05-27 18:02:55 +00:00
|
|
|
}
|
|
|
|
|
2016-12-09 18:35:02 +00:00
|
|
|
rtContext->priv().absClear(nullptr, 0x0);
|
2015-05-27 18:02:55 +00:00
|
|
|
|
2017-01-11 18:42:54 +00:00
|
|
|
GrPaint maskPaint;
|
|
|
|
maskPaint.setCoverageSetOpXPFactory(SkRegion::kReplace_Op);
|
2015-05-27 18:02:55 +00:00
|
|
|
|
|
|
|
// setup new clip
|
2016-05-13 17:25:00 +00:00
|
|
|
const SkIRect clipRect = SkIRect::MakeWH(maskRect.width(), maskRect.height());
|
|
|
|
GrFixedClip clip(clipRect);
|
2015-05-27 18:02:55 +00:00
|
|
|
|
2015-09-14 18:18:13 +00:00
|
|
|
// Draw the mask into maskTexture with the path's integerized top-left at
|
2017-01-11 18:42:54 +00:00
|
|
|
// the origin using maskPaint.
|
2015-05-27 18:02:55 +00:00
|
|
|
SkMatrix translate;
|
2016-05-13 12:06:19 +00:00
|
|
|
translate.setTranslate(-SkIntToScalar(maskRect.fLeft), -SkIntToScalar(maskRect.fTop));
|
2017-01-11 18:42:54 +00:00
|
|
|
rtContext->drawPath(clip, std::move(maskPaint), aa, translate, devPath,
|
|
|
|
GrStyle(fillOrHairline));
|
2017-01-30 18:27:37 +00:00
|
|
|
return rtContext->asTextureProxyRef();
|
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,
|
2016-10-27 18:47:55 +00:00
|
|
|
GrRenderTargetContext* renderTargetContext,
|
2015-11-09 19:55:57 +00:00
|
|
|
const GrClip& clip,
|
2017-01-11 18:42:54 +00:00
|
|
|
GrPaint&& paint,
|
2016-12-09 20:10:07 +00:00
|
|
|
GrAA aa,
|
2015-11-09 19:55:57 +00:00
|
|
|
const SkMatrix& viewMatrix,
|
2018-01-23 20:29:32 +00:00
|
|
|
const SkMaskFilterBase* maskFilter,
|
2016-05-10 16:14:17 +00:00
|
|
|
const GrStyle& style,
|
|
|
|
const SkPath* path,
|
2015-11-09 19:55:57 +00:00
|
|
|
bool pathIsMutable) {
|
|
|
|
SkASSERT(maskFilter);
|
|
|
|
|
|
|
|
SkIRect clipBounds;
|
2016-12-09 18:35:02 +00:00
|
|
|
clip.getConservativeBounds(renderTargetContext->width(),
|
|
|
|
renderTargetContext->height(),
|
2016-10-27 18:47:55 +00:00
|
|
|
&clipBounds);
|
2015-11-09 19:55:57 +00:00
|
|
|
SkTLazy<SkPath> tmpPath;
|
2016-05-10 16:14:17 +00:00
|
|
|
SkStrokeRec::InitStyle fillOrHairline;
|
2015-11-09 19:55:57 +00:00
|
|
|
|
2016-05-10 16:14:17 +00:00
|
|
|
// We just fully apply the style here.
|
|
|
|
if (style.applies()) {
|
2016-08-19 15:07:22 +00:00
|
|
|
SkScalar scale = GrStyle::MatrixToScaleFactor(viewMatrix);
|
|
|
|
if (0 == scale || !style.applyToPath(tmpPath.init(), &fillOrHairline, *path, scale)) {
|
2016-05-10 16:14:17 +00:00
|
|
|
return;
|
2016-05-10 13:19:21 +00:00
|
|
|
}
|
2016-05-10 16:14:17 +00:00
|
|
|
pathIsMutable = true;
|
|
|
|
path = tmpPath.get();
|
|
|
|
} else if (style.isSimpleHairline()) {
|
|
|
|
fillOrHairline = SkStrokeRec::kHairline_InitStyle;
|
|
|
|
} else {
|
|
|
|
SkASSERT(style.isSimpleFill());
|
|
|
|
fillOrHairline = SkStrokeRec::kFill_InitStyle;
|
2016-05-10 12:57:27 +00:00
|
|
|
}
|
2015-11-09 19:55:57 +00:00
|
|
|
|
2016-05-10 13:19:21 +00:00
|
|
|
// transform the path into device space
|
2016-05-10 16:14:17 +00:00
|
|
|
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;
|
|
|
|
}
|
2016-05-10 13:19:21 +00:00
|
|
|
|
2015-11-09 19:55:57 +00:00
|
|
|
SkRect maskRect;
|
2016-05-10 16:14:17 +00:00
|
|
|
if (maskFilter->canFilterMaskGPU(SkRRect::MakeRect(path->getBounds()),
|
2015-11-09 19:55:57 +00:00
|
|
|
clipBounds,
|
|
|
|
viewMatrix,
|
|
|
|
&maskRect)) {
|
2016-05-13 12:06:19 +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
|
|
|
|
// so the mask draws in a reproducible manner.
|
2015-11-09 19:55:57 +00:00
|
|
|
SkIRect finalIRect;
|
|
|
|
maskRect.roundOut(&finalIRect);
|
|
|
|
if (clip_bounds_quick_reject(clipBounds, finalIRect)) {
|
|
|
|
// clipped out
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-03-14 13:17:43 +00:00
|
|
|
if (maskFilter->directFilterMaskGPU(context,
|
2016-10-27 18:47:55 +00:00
|
|
|
renderTargetContext,
|
2017-01-11 18:42:54 +00:00
|
|
|
std::move(paint),
|
2015-11-09 19:55:57 +00:00
|
|
|
clip,
|
|
|
|
viewMatrix,
|
2016-05-10 16:14:17 +00:00
|
|
|
SkStrokeRec(fillOrHairline),
|
|
|
|
*path)) {
|
2015-11-09 19:55:57 +00:00
|
|
|
// the mask filter was able to draw itself directly, so there's nothing
|
|
|
|
// left to do.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-11-21 19:05:03 +00:00
|
|
|
sk_sp<GrTextureProxy> maskProxy(create_mask_GPU(context,
|
|
|
|
finalIRect,
|
|
|
|
*path,
|
|
|
|
fillOrHairline,
|
2016-12-09 20:10:07 +00:00
|
|
|
aa,
|
2016-11-21 19:05:03 +00:00
|
|
|
renderTargetContext->numColorSamples()));
|
|
|
|
if (maskProxy) {
|
2016-12-14 14:00:07 +00:00
|
|
|
sk_sp<GrTextureProxy> filtered = maskFilter->filterMaskGPU(context,
|
|
|
|
std::move(maskProxy),
|
|
|
|
viewMatrix,
|
|
|
|
finalIRect);
|
|
|
|
if (filtered) {
|
2017-03-15 14:42:12 +00:00
|
|
|
if (draw_mask(renderTargetContext, clip, viewMatrix,
|
2017-01-11 18:42:54 +00:00
|
|
|
finalIRect, std::move(paint), std::move(filtered))) {
|
2015-11-09 19:55:57 +00:00
|
|
|
// This path is completely drawn
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-11 18:42:54 +00:00
|
|
|
sw_draw_with_mask_filter(context, renderTargetContext, clip, viewMatrix, *path, maskFilter,
|
|
|
|
clipBounds, std::move(paint), fillOrHairline);
|
2015-11-09 19:55:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void GrBlurUtils::drawPathWithMaskFilter(GrContext* context,
|
2016-10-27 18:47:55 +00:00
|
|
|
GrRenderTargetContext* renderTargetContext,
|
2015-11-09 19:55:57 +00:00
|
|
|
const GrClip& clip,
|
2016-05-10 16:14:17 +00:00
|
|
|
const SkPath& path,
|
2017-01-11 18:42:54 +00:00
|
|
|
GrPaint&& paint,
|
2016-12-09 20:10:07 +00:00
|
|
|
GrAA aa,
|
2015-11-09 19:55:57 +00:00
|
|
|
const SkMatrix& viewMatrix,
|
|
|
|
const SkMaskFilter* mf,
|
2016-05-10 16:14:17 +00:00
|
|
|
const GrStyle& style,
|
2015-11-30 13:45:06 +00:00
|
|
|
bool pathIsMutable) {
|
2017-01-11 18:42:54 +00:00
|
|
|
draw_path_with_mask_filter(context, renderTargetContext, clip, std::move(paint), aa, viewMatrix,
|
2018-01-23 20:29:32 +00:00
|
|
|
as_MFB(mf), style, &path, pathIsMutable);
|
2015-11-09 19:55:57 +00:00
|
|
|
}
|
|
|
|
|
2016-03-29 16:03:52 +00:00
|
|
|
void GrBlurUtils::drawPathWithMaskFilter(GrContext* context,
|
2016-10-27 18:47:55 +00:00
|
|
|
GrRenderTargetContext* renderTargetContext,
|
2015-05-27 18:02:55 +00:00
|
|
|
const GrClip& clip,
|
2016-05-10 16:14:17 +00:00
|
|
|
const SkPath& origPath,
|
2015-05-27 18:02:55 +00:00
|
|
|
const SkPaint& paint,
|
|
|
|
const SkMatrix& origViewMatrix,
|
|
|
|
const SkMatrix* prePathMatrix,
|
|
|
|
const SkIRect& clipBounds,
|
|
|
|
bool pathIsMutable) {
|
2018-03-08 16:07:23 +00:00
|
|
|
if (context->contextPriv().abandoned()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-05-10 16:14:17 +00:00
|
|
|
SkASSERT(!pathIsMutable || origPath.isVolatile());
|
2015-05-27 18:02:55 +00:00
|
|
|
|
2016-05-10 16:14:17 +00:00
|
|
|
GrStyle style(paint);
|
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).
|
2016-05-10 16:14:17 +00:00
|
|
|
|
|
|
|
const SkPath* path = &origPath;
|
2015-05-27 18:02:55 +00:00
|
|
|
SkTLazy<SkPath> tmpPath;
|
|
|
|
|
|
|
|
SkMatrix viewMatrix = origViewMatrix;
|
|
|
|
|
|
|
|
if (prePathMatrix) {
|
2016-05-10 16:14:17 +00:00
|
|
|
// Styling, blurs, and shading are supposed to be applied *after* the prePathMatrix.
|
|
|
|
if (!paint.getMaskFilter() && !paint.getShader() && !style.applies()) {
|
2015-05-27 18:02:55 +00:00
|
|
|
viewMatrix.preConcat(*prePathMatrix);
|
|
|
|
} else {
|
2016-05-10 16:14:17 +00:00
|
|
|
SkPath* result = pathIsMutable ? const_cast<SkPath*>(path) : tmpPath.init();
|
|
|
|
pathIsMutable = true;
|
|
|
|
path->transform(*prePathMatrix, result);
|
|
|
|
path = result;
|
|
|
|
result->setIsVolatile(true);
|
2015-05-27 18:02:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// at this point we're done with prePathMatrix
|
|
|
|
SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
|
|
|
|
|
|
|
|
GrPaint grPaint;
|
2017-10-24 16:52:33 +00:00
|
|
|
if (!SkPaintToGrPaint(context, renderTargetContext->colorSpaceInfo(), paint, viewMatrix,
|
|
|
|
&grPaint)) {
|
2015-05-27 18:02:55 +00:00
|
|
|
return;
|
|
|
|
}
|
2017-11-27 21:33:06 +00:00
|
|
|
GrAA aa = GrAA(paint.isAntiAlias());
|
2018-01-23 20:29:32 +00:00
|
|
|
SkMaskFilterBase* mf = as_MFB(paint.getMaskFilter());
|
2018-01-20 22:24:21 +00:00
|
|
|
if (mf && !mf->hasFragmentProcessor()) {
|
2016-10-20 13:40:55 +00:00
|
|
|
// The MaskFilter wasn't already handled in SkPaintToGrPaint
|
2017-01-11 18:42:54 +00:00
|
|
|
draw_path_with_mask_filter(context, renderTargetContext, clip, std::move(grPaint), aa,
|
|
|
|
viewMatrix, mf, style, path, pathIsMutable);
|
2015-11-09 19:55:57 +00:00
|
|
|
} else {
|
2017-01-11 18:42:54 +00:00
|
|
|
renderTargetContext->drawPath(clip, std::move(grPaint), aa, viewMatrix, *path, style);
|
2015-05-27 18:02:55 +00:00
|
|
|
}
|
|
|
|
}
|