/* * 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 "GrCaps.h" #include "GrContext.h" #include "GrContextPriv.h" #include "GrFixedClip.h" #include "GrProxyProvider.h" #include "GrRenderTargetContext.h" #include "GrRenderTargetContextPriv.h" #include "GrStyle.h" #include "GrTextureProxy.h" #include "effects/GrSimpleTextureEffect.h" #include "SkDraw.h" #include "SkGr.h" #include "SkMaskFilterBase.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(GrRenderTargetContext* renderTargetContext, const GrClip& clip, const SkMatrix& viewMatrix, const SkIRect& maskRect, GrPaint&& paint, sk_sp mask) { SkMatrix inverse; if (!viewMatrix.invert(&inverse)) { return false; } SkMatrix matrix = SkMatrix::MakeTrans(-SkIntToScalar(maskRect.fLeft), -SkIntToScalar(maskRect.fTop)); matrix.preConcat(viewMatrix); paint.addCoverageFragmentProcessor(GrSimpleTextureEffect::Make(std::move(mask), matrix)); renderTargetContext->fillRectWithLocalMatrix(clip, std::move(paint), GrAA::kNo, SkMatrix::I(), SkRect::Make(maskRect), inverse); return true; } static void mask_release_proc(void* addr, void* /*context*/) { SkMask::FreeImage(addr); } static bool sw_draw_with_mask_filter(GrContext* context, GrRenderTargetContext* renderTargetContext, const GrClip& clipData, const SkMatrix& viewMatrix, const SkPath& devPath, const SkMaskFilter* filter, const SkIRect& clipBounds, GrPaint&& paint, 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 (!as_MFB(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 SkBitmap bm; if (!bm.installPixels(SkImageInfo::MakeA8(dstM.fBounds.width(), dstM.fBounds.height()), autoDst.release(), dstM.fRowBytes, mask_release_proc, nullptr)) { return false; } bm.setImmutable(); sk_sp image = SkImage::MakeFromBitmap(bm); if (!image) { return false; } auto proxyProvider = context->contextPriv().proxyProvider(); sk_sp maskProxy = proxyProvider->createTextureProxy(std::move(image), kNone_GrSurfaceFlags, 1, SkBudgeted::kYes, SkBackingFit::kApprox); if (!maskProxy) { return false; } return draw_mask(renderTargetContext, clipData, viewMatrix, dstM.fBounds, std::move(paint), std::move(maskProxy)); } // Create a mask of 'devPath' and place the result in 'mask'. static sk_sp create_mask_GPU(GrContext* context, const SkIRect& maskRect, const SkPath& devPath, SkStrokeRec::InitStyle fillOrHairline, GrAA aa, int sampleCnt) { if (GrAA::kNo == aa) { // Don't need MSAA if mask isn't AA sampleCnt = 1; } sk_sp rtContext( context->contextPriv().makeDeferredRenderTargetContextWithFallback( SkBackingFit::kApprox, maskRect.width(), maskRect.height(), kAlpha_8_GrPixelConfig, nullptr, sampleCnt)); if (!rtContext) { return nullptr; } rtContext->priv().absClear(nullptr, 0x0); GrPaint maskPaint; maskPaint.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 maskPaint. SkMatrix translate; translate.setTranslate(-SkIntToScalar(maskRect.fLeft), -SkIntToScalar(maskRect.fTop)); rtContext->drawPath(clip, std::move(maskPaint), aa, translate, devPath, GrStyle(fillOrHairline)); return rtContext->asTextureProxyRef(); } static void draw_path_with_mask_filter(GrContext* context, GrRenderTargetContext* renderTargetContext, const GrClip& clip, GrPaint&& paint, GrAA aa, const SkMatrix& viewMatrix, const SkMaskFilterBase* maskFilter, const GrStyle& style, const SkPath* path, bool pathIsMutable) { SkASSERT(maskFilter); SkIRect clipBounds; clip.getConservativeBounds(renderTargetContext->width(), renderTargetContext->height(), &clipBounds); SkTLazy 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(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, renderTargetContext, std::move(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 maskProxy(create_mask_GPU(context, finalIRect, *path, fillOrHairline, aa, renderTargetContext->numColorSamples())); if (maskProxy) { sk_sp filtered = maskFilter->filterMaskGPU(context, std::move(maskProxy), viewMatrix, finalIRect); if (filtered) { if (draw_mask(renderTargetContext, clip, viewMatrix, finalIRect, std::move(paint), std::move(filtered))) { // This path is completely drawn return; } } } } sw_draw_with_mask_filter(context, renderTargetContext, clip, viewMatrix, *path, maskFilter, clipBounds, std::move(paint), fillOrHairline); } void GrBlurUtils::drawPathWithMaskFilter(GrContext* context, GrRenderTargetContext* renderTargetContext, const GrClip& clip, const SkPath& path, GrPaint&& paint, GrAA aa, const SkMatrix& viewMatrix, const SkMaskFilter* mf, const GrStyle& style, bool pathIsMutable) { draw_path_with_mask_filter(context, renderTargetContext, clip, std::move(paint), aa, viewMatrix, as_MFB(mf), style, &path, pathIsMutable); } void GrBlurUtils::drawPathWithMaskFilter(GrContext* context, GrRenderTargetContext* renderTargetContext, const GrClip& clip, const SkPath& origPath, const SkPaint& paint, const SkMatrix& origViewMatrix, const SkMatrix* prePathMatrix, const SkIRect& clipBounds, bool pathIsMutable) { if (context->abandoned()) { return; } 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 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(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, renderTargetContext->colorSpaceInfo(), paint, viewMatrix, &grPaint)) { return; } GrAA aa = GrAA(paint.isAntiAlias()); SkMaskFilterBase* mf = as_MFB(paint.getMaskFilter()); if (mf && !mf->hasFragmentProcessor()) { // The MaskFilter wasn't already handled in SkPaintToGrPaint draw_path_with_mask_filter(context, renderTargetContext, clip, std::move(grPaint), aa, viewMatrix, mf, style, path, pathIsMutable); } else { renderTargetContext->drawPath(clip, std::move(grPaint), aa, viewMatrix, *path, style); } }