diff --git a/gm/savelayer.cpp b/gm/savelayer.cpp index 537bf86d38..22461770eb 100644 --- a/gm/savelayer.cpp +++ b/gm/savelayer.cpp @@ -6,6 +6,7 @@ */ #include "SkCanvasPriv.h" +#include "SkShaderMaskFilter.h" #include "ToolUtils.h" #include "gm.h" @@ -276,6 +277,63 @@ DEF_SIMPLE_GM(savelayer_coverage, canvas, 500, 500) { canvas->restore(); } +DEF_SIMPLE_GM(savelayer_clipmask_maskfilter, canvas, 500, 500) { + // Offscreen surface for making the clip mask and mask filter images + auto surf = SkSurface::MakeRaster(SkImageInfo::MakeA8(100, 100)); + SkPaint maskPaint; + maskPaint.setColor(SK_ColorWHITE); + maskPaint.setAntiAlias(true); + + // Draw a centered circle for the mask filter + surf->getCanvas()->clear(SK_ColorTRANSPARENT); + surf->getCanvas()->drawCircle(50.f, 50.f, 50.f, maskPaint); + auto maskFilterImage = surf->makeImageSnapshot(); + sk_sp maskFilter = SkShaderMaskFilter::Make(maskFilterImage->makeShader()); + + // Cut out a cross for the clip mask + surf->getCanvas()->clear(SK_ColorTRANSPARENT); + surf->getCanvas()->drawRect(SkRect::MakeLTRB(0.f, 0.f, 40.f, 40.f), maskPaint); + surf->getCanvas()->drawRect(SkRect::MakeLTRB(60.f, 0.f, 100.f, 40.f), maskPaint); + surf->getCanvas()->drawRect(SkRect::MakeLTRB(0.f, 60.f, 40.f, 100.f), maskPaint); + surf->getCanvas()->drawRect(SkRect::MakeLTRB(60.f, 60.f, 100.f, 100.f), maskPaint); + auto clipMaskImage = surf->makeImageSnapshot(); + SkMatrix clipMatrix = SkMatrix::I(); + SkRect clipBounds = SkRect::MakeWH(100, 100); + + // On the main canvas, save a 100x100 layer three times, applying clip mask, mask filter, or + // both, translating across the GM for each configuration. + canvas->clear(SK_ColorGRAY); + + canvas->translate(25.f, 0.f); + + // Clip mask only + SkCanvas::SaveLayerRec rec; + rec.fBounds = &clipBounds; + rec.fClipMask = clipMaskImage.get(); + rec.fClipMatrix = &clipMatrix; + canvas->saveLayer(rec); + canvas->clear(SK_ColorWHITE); + canvas->restore(); + + canvas->translate(125.f, 0.f); + + // Mask filter only + maskPaint.setMaskFilter(maskFilter); + rec.fClipMask = nullptr; + rec.fPaint = &maskPaint; + canvas->saveLayer(rec); + canvas->clear(SK_ColorWHITE); + canvas->restore(); + + canvas->translate(125.f, 0.f); + + // Both + rec.fClipMask = clipMaskImage.get(); + canvas->saveLayer(rec); + canvas->clear(SK_ColorWHITE); + canvas->restore(); +} + #include "SkFont.h" #include "SkGradientShader.h" #include "SkTextBlob.h" diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h index d66fd8d728..34461b5915 100644 --- a/include/core/SkCanvas.h +++ b/include/core/SkCanvas.h @@ -625,8 +625,6 @@ public: clipMatrix uses alpha channel of image, transformed by clipMatrix, to clip layer when drawn to SkCanvas. - Implementation is not complete; has no effect if SkBaseDevice is GPU-backed. - @param bounds layer dimensions; may be nullptr @param paint graphics state applied to layer when overlaying prior layer; may be nullptr diff --git a/infra/bots/recipes/test.expected/Test-Win10-Clang-NUC8i5BEK-GPU-IntelIris655-x86_64-Debug-All-Vulkan.json b/infra/bots/recipes/test.expected/Test-Win10-Clang-NUC8i5BEK-GPU-IntelIris655-x86_64-Debug-All-Vulkan.json index 66339f8340..16984757bf 100644 --- a/infra/bots/recipes/test.expected/Test-Win10-Clang-NUC8i5BEK-GPU-IntelIris655-x86_64-Debug-All-Vulkan.json +++ b/infra/bots/recipes/test.expected/Test-Win10-Clang-NUC8i5BEK-GPU-IntelIris655-x86_64-Debug-All-Vulkan.json @@ -416,6 +416,10 @@ "vk", "gm", "_", + "savelayer_clipmask", + "vk", + "gm", + "_", "aarectmodes", "vk", "gm", diff --git a/infra/bots/recipes/test.py b/infra/bots/recipes/test.py index 07ec2738a2..600ed2b074 100644 --- a/infra/bots/recipes/test.py +++ b/infra/bots/recipes/test.py @@ -668,6 +668,8 @@ def dm_flags(api, bot): match.append('~FloatingPointTextureTest$') if 'Vulkan' in bot and 'Win10' in bot and 'IntelIris655' in bot: + # skia:8961 + blacklist(['vk', 'gm', '_', 'savelayer_clipmask']) # skia:8659 blacklist(['vk', 'gm', '_', 'aarectmodes']) blacklist(['vk', 'gm', '_', 'aaxfermodes']) diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index 91fd686c7b..12af310088 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -21,6 +21,7 @@ #include "GrTextureAdjuster.h" #include "GrTracing.h" #include "SkCanvasPriv.h" +#include "SkClipStack.h" #include "SkDraw.h" #include "SkGr.h" #include "SkImageFilter.h" @@ -1021,8 +1022,6 @@ void SkGpuDevice::drawSpecial(SkSpecialImage* special, int left, int top, const ASSERT_SINGLE_OWNER GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawSpecial", fContext.get()); - // TODO: clipImage support. - sk_sp result; if (paint.getImageFilter()) { SkIPoint offset = { 0, 0 }; @@ -1046,10 +1045,11 @@ void SkGpuDevice::drawSpecial(SkSpecialImage* special, int left, int top, const const GrPixelConfig config = proxy->config(); + SkMatrix ctm = this->ctm(); + ctm.postTranslate(-SkIntToScalar(left), -SkIntToScalar(top)); + SkPaint tmpUnfiltered(paint); if (tmpUnfiltered.getMaskFilter()) { - SkMatrix ctm = this->ctm(); - ctm.postTranslate(-SkIntToScalar(left), -SkIntToScalar(top)); tmpUnfiltered.setMaskFilter(tmpUnfiltered.getMaskFilter()->makeWithMatrix(ctm)); } @@ -1071,14 +1071,57 @@ void SkGpuDevice::drawSpecial(SkSpecialImage* special, int left, int top, const } const SkIRect& subset = result->subset(); + SkRect dstRect = SkRect::Make(SkIRect::MakeXYWH(left, top, subset.width(), subset.height())); + SkRect srcRect = SkRect::Make(subset); + if (clipImage) { + // Add the image as a simple texture effect applied to coverage. Accessing content outside + // of the clip image should behave as if it were a decal (i.e. zero coverage). However, to + // limit pixels touched and hardware checks, we draw the clip image geometry to get the + // decal effect. + GrSamplerState sampler = paint.getFilterQuality() > kNone_SkFilterQuality ? + GrSamplerState::ClampBilerp() : GrSamplerState::ClampNearest(); + sk_sp clipProxy = as_IB(clipImage)->asTextureProxyRef(this->context(), + sampler, nullptr); + // Fold clip matrix into ctm + ctm.preConcat(clipMatrix); + SkMatrix inverseClipMatrix; - fRenderTargetContext->fillRectToRect( - this->clip(), - std::move(grPaint), - GrAA(tmpUnfiltered.isAntiAlias()), - SkMatrix::I(), - SkRect::Make(SkIRect::MakeXYWH(left, top, subset.width(), subset.height())), - SkRect::Make(subset)); + std::unique_ptr cfp; + if (clipProxy && ctm.invert(&inverseClipMatrix)) { + cfp = GrSimpleTextureEffect::Make(std::move(clipProxy), inverseClipMatrix, sampler); + if (clipImage->colorType() != kAlpha_8_SkColorType) { + cfp = GrFragmentProcessor::SwizzleOutput(std::move(cfp), GrSwizzle::AAAA()); + } + } + + if (cfp) { + // If the grPaint already has coverage, this adds an additional stage that multiples + // the image's alpha channel with the prior coverage. + grPaint.addCoverageFragmentProcessor(std::move(cfp)); + + // Undo the offset that was needed for shader coord transforms to get the transform for + // the actual drawn geometry. + ctm.postTranslate(SkIntToScalar(left), SkIntToScalar(top)); + inverseClipMatrix.preTranslate(-SkIntToScalar(left), -SkIntToScalar(top)); + SkRect clipGeometry = SkRect::MakeWH(clipImage->width(), clipImage->height()); + if (!clipGeometry.contains(inverseClipMatrix.mapRect(dstRect))) { + // Draw the clip geometry since it is smaller, using dstRect as an extra scissor + SkClipStack clip(this->cs()); + clip.clipDevRect(SkIRect::MakeXYWH(left, top, subset.width(), subset.height()), + SkClipOp::kIntersect); + SkMatrix local = SkMatrix::Concat(SkMatrix::MakeRectToRect( + dstRect, srcRect, SkMatrix::kFill_ScaleToFit), ctm); + fRenderTargetContext->fillRectWithLocalMatrix(GrClipStackClip(&clip), + std::move(grPaint), GrAA(paint.isAntiAlias()), ctm, clipGeometry, local); + return; + } + // Else fall through and draw the subset since that is contained in the clip geometry + } + // Else some issue configuring the coverage FP, so just draw without the clip mask image + } + // Draw directly in screen space, possibly with an extra coverage processor + fRenderTargetContext->fillRectToRect(this->clip(), std::move(grPaint), + GrAA(paint.isAntiAlias()), SkMatrix::I(), dstRect, srcRect); } void SkGpuDevice::drawBitmapRect(const SkBitmap& bitmap,