sk_sp<GrTexture> GrClipMaskManager::CreateSoftwareClipMask( GrTextureProvider* texProvider, int32_t elementsGenID, GrReducedClip::InitialState initialState, const GrReducedClip::ElementList& elements, const SkVector& clipToMaskOffset, const SkIRect& clipSpaceIBounds) { GrUniqueKey key; GetClipMaskKey(elementsGenID, clipSpaceIBounds, &key); if (GrTexture* texture = texProvider->findAndRefTextureByUniqueKey(key)) { return sk_sp<GrTexture>(texture); } // The mask texture may be larger than necessary. We round out the clip space bounds and pin // the top left corner of the resulting rect to the top left of the texture. SkIRect maskSpaceIBounds = SkIRect::MakeWH(clipSpaceIBounds.width(), clipSpaceIBounds.height()); GrSWMaskHelper helper(texProvider); // Set the matrix so that rendered clip elements are transformed to mask space from clip // space. SkMatrix translate; translate.setTranslate(clipToMaskOffset); helper.init(maskSpaceIBounds, &translate); helper.clear(GrReducedClip::kAllIn_InitialState == initialState ? 0xFF : 0x00); for (GrReducedClip::ElementList::Iter iter(elements.headIter()) ; iter.get(); iter.next()) { const Element* element = iter.get(); SkRegion::Op op = element->getOp(); if (SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) { // Intersect and reverse difference require modifying pixels outside of the geometry // that is being "drawn". In both cases we erase all the pixels outside of the geometry // but leave the pixels inside the geometry alone. For reverse difference we invert all // the pixels before clearing the ones outside the geometry. if (SkRegion::kReverseDifference_Op == op) { SkRect temp = SkRect::Make(clipSpaceIBounds); // invert the entire scene helper.drawRect(temp, SkRegion::kXOR_Op, false, 0xFF); } SkPath clipPath; element->asPath(&clipPath); clipPath.toggleInverseFillType(); GrShape shape(clipPath, GrStyle::SimpleFill()); helper.drawShape(shape, SkRegion::kReplace_Op, element->isAA(), 0x00); continue; } // The other ops (union, xor, diff) only affect pixels inside // the geometry so they can just be drawn normally if (Element::kRect_Type == element->getType()) { helper.drawRect(element->getRect(), op, element->isAA(), 0xFF); } else { SkPath path; element->asPath(&path); GrShape shape(path, GrStyle::SimpleFill()); helper.drawShape(shape, op, element->isAA(), 0xFF); } } // Allocate clip mask texture GrSurfaceDesc desc; desc.fWidth = clipSpaceIBounds.width(); desc.fHeight = clipSpaceIBounds.height(); desc.fConfig = kAlpha_8_GrPixelConfig; sk_sp<GrTexture> result(texProvider->createApproxTexture(desc)); if (!result) { return nullptr; } result->resourcePriv().setUniqueKey(key); helper.toTexture(result.get()); return result; }
GrTexture* GrClipMaskManager::createAlphaClipMask(int32_t elementsGenID, GrReducedClip::InitialState initialState, const GrReducedClip::ElementList& elements, const SkVector& clipToMaskOffset, const SkIRect& clipSpaceIBounds) { GrResourceProvider* resourceProvider = fDrawTarget->cmmAccess().resourceProvider(); GrUniqueKey key; GetClipMaskKey(elementsGenID, clipSpaceIBounds, &key); if (GrTexture* texture = resourceProvider->findAndRefTextureByUniqueKey(key)) { return texture; } SkAutoTUnref<GrTexture> texture(this->createCachedMask( clipSpaceIBounds.width(), clipSpaceIBounds.height(), key, true)); // There's no texture in the cache. Let's try to allocate it then. if (!texture) { return nullptr; } // Set the matrix so that rendered clip elements are transformed to mask space from clip // space. SkMatrix translate; translate.setTranslate(clipToMaskOffset); // The texture may be larger than necessary, this rect represents the part of the texture // we populate with a rasterization of the clip. SkIRect maskSpaceIBounds = SkIRect::MakeWH(clipSpaceIBounds.width(), clipSpaceIBounds.height()); // The scratch texture that we are drawing into can be substantially larger than the mask. Only // clear the part that we care about. fDrawTarget->clear(&maskSpaceIBounds, GrReducedClip::kAllIn_InitialState == initialState ? 0xffffffff : 0x00000000, true, texture->asRenderTarget()); // When we use the stencil in the below loop it is important to have this clip installed. // The second pass that zeros the stencil buffer renders the rect maskSpaceIBounds so the first // pass must not set values outside of this bounds or stencil values outside the rect won't be // cleared. GrClip clip(maskSpaceIBounds); SkAutoTUnref<GrTexture> temp; // walk through each clip element and perform its set op for (GrReducedClip::ElementList::Iter iter = elements.headIter(); iter.get(); iter.next()) { const Element* element = iter.get(); SkRegion::Op op = element->getOp(); bool invert = element->isInverseFilled(); if (invert || SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) { GrPipelineBuilder pipelineBuilder; pipelineBuilder.setClip(clip); GrPathRenderer* pr = nullptr; bool useTemp = !this->canStencilAndDrawElement(&pipelineBuilder, texture, &pr, element); GrTexture* dst; // This is the bounds of the clip element in the space of the alpha-mask. The temporary // mask buffer can be substantially larger than the actually clip stack element. We // touch the minimum number of pixels necessary and use decal mode to combine it with // the accumulator. SkIRect maskSpaceElementIBounds; if (useTemp) { if (invert) { maskSpaceElementIBounds = maskSpaceIBounds; } else { SkRect elementBounds = element->getBounds(); elementBounds.offset(clipToMaskOffset); elementBounds.roundOut(&maskSpaceElementIBounds); } if (!temp) { temp.reset(this->createTempMask(maskSpaceIBounds.fRight, maskSpaceIBounds.fBottom)); if (!temp) { texture->resourcePriv().removeUniqueKey(); return nullptr; } } dst = temp; // clear the temp target and set blend to replace fDrawTarget->clear(&maskSpaceElementIBounds, invert ? 0xffffffff : 0x00000000, true, dst->asRenderTarget()); set_coverage_drawing_xpf(SkRegion::kReplace_Op, invert, &pipelineBuilder); } else { // draw directly into the result with the stencil set to make the pixels affected // by the clip shape be non-zero. dst = texture; GR_STATIC_CONST_SAME_STENCIL(kStencilInElement, kReplace_StencilOp, kReplace_StencilOp, kAlways_StencilFunc, 0xffff, 0xffff, 0xffff); pipelineBuilder.setStencil(kStencilInElement); set_coverage_drawing_xpf(op, invert, &pipelineBuilder); } if (!this->drawElement(&pipelineBuilder, translate, dst, element, pr)) { texture->resourcePriv().removeUniqueKey(); return nullptr; } if (useTemp) { GrPipelineBuilder backgroundPipelineBuilder; backgroundPipelineBuilder.setRenderTarget(texture->asRenderTarget()); // Now draw into the accumulator using the real operation and the temp buffer as a // texture this->mergeMask(&backgroundPipelineBuilder, texture, temp, op, maskSpaceIBounds, maskSpaceElementIBounds); } else { GrPipelineBuilder backgroundPipelineBuilder; backgroundPipelineBuilder.setRenderTarget(texture->asRenderTarget()); set_coverage_drawing_xpf(op, !invert, &backgroundPipelineBuilder); // Draw to the exterior pixels (those with a zero stencil value). GR_STATIC_CONST_SAME_STENCIL(kDrawOutsideElement, kZero_StencilOp, kZero_StencilOp, kEqual_StencilFunc, 0xffff, 0x0000, 0xffff); backgroundPipelineBuilder.setStencil(kDrawOutsideElement); // The color passed in here does not matter since the coverageSetOpXP won't read it. fDrawTarget->drawNonAARect(backgroundPipelineBuilder, GrColor_WHITE, translate, clipSpaceIBounds); } } else { GrPipelineBuilder pipelineBuilder; // all the remaining ops can just be directly draw into the accumulation buffer set_coverage_drawing_xpf(op, false, &pipelineBuilder); // The color passed in here does not matter since the coverageSetOpXP won't read it. this->drawElement(&pipelineBuilder, translate, texture, element); } } return texture.detach(); }
sk_sp<GrTexture> GrClipMaskManager::CreateAlphaClipMask(GrContext* context, int32_t elementsGenID, GrReducedClip::InitialState initialState, const GrReducedClip::ElementList& elements, const SkVector& clipToMaskOffset, const SkIRect& clipSpaceIBounds) { GrResourceProvider* resourceProvider = context->resourceProvider(); GrUniqueKey key; GetClipMaskKey(elementsGenID, clipSpaceIBounds, &key); if (GrTexture* texture = resourceProvider->findAndRefTextureByUniqueKey(key)) { return sk_sp<GrTexture>(texture); } // There's no texture in the cache. Let's try to allocate it then. GrPixelConfig config = kRGBA_8888_GrPixelConfig; if (context->caps()->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) { config = kAlpha_8_GrPixelConfig; } sk_sp<GrDrawContext> dc(context->newDrawContext(SkBackingFit::kApprox, clipSpaceIBounds.width(), clipSpaceIBounds.height(), config)); if (!dc) { return nullptr; } // The texture may be larger than necessary, this rect represents the part of the texture // we populate with a rasterization of the clip. SkIRect maskSpaceIBounds = SkIRect::MakeWH(clipSpaceIBounds.width(), clipSpaceIBounds.height()); // The scratch texture that we are drawing into can be substantially larger than the mask. Only // clear the part that we care about. dc->clear(&maskSpaceIBounds, GrReducedClip::kAllIn_InitialState == initialState ? 0xffffffff : 0x00000000, true); // Set the matrix so that rendered clip elements are transformed to mask space from clip // space. const SkMatrix translate = SkMatrix::MakeTrans(clipToMaskOffset.fX, clipToMaskOffset.fY); // It is important that we use maskSpaceIBounds as the stencil rect in the below loop. // The second pass that zeros the stencil buffer renders the rect maskSpaceIBounds so the first // pass must not set values outside of this bounds or stencil values outside the rect won't be // cleared. // walk through each clip element and perform its set op for (GrReducedClip::ElementList::Iter iter = elements.headIter(); iter.get(); iter.next()) { const Element* element = iter.get(); SkRegion::Op op = element->getOp(); bool invert = element->isInverseFilled(); if (invert || SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) { GrFixedClip clip(maskSpaceIBounds); // draw directly into the result with the stencil set to make the pixels affected // by the clip shape be non-zero. static constexpr GrUserStencilSettings kStencilInElement( GrUserStencilSettings::StaticInit< 0xffff, GrUserStencilTest::kAlways, 0xffff, GrUserStencilOp::kReplace, GrUserStencilOp::kReplace, 0xffff>() ); if (!stencil_element(dc.get(), clip, &kStencilInElement, translate, element)) { return nullptr; } // Draw to the exterior pixels (those with a zero stencil value). static constexpr GrUserStencilSettings kDrawOutsideElement( GrUserStencilSettings::StaticInit< 0x0000, GrUserStencilTest::kEqual, 0xffff, GrUserStencilOp::kZero, GrUserStencilOp::kZero, 0xffff>() ); if (!dc->drawContextPriv().drawAndStencilRect(clip, &kDrawOutsideElement, op, !invert, false, translate, SkRect::Make(clipSpaceIBounds))) { return nullptr; } } else { // all the remaining ops can just be directly draw into the accumulation buffer GrPaint paint; paint.setAntiAlias(element->isAA()); paint.setCoverageSetOpXPFactory(op, false); draw_element(dc.get(), GrNoClip(), paint, translate, element); } } sk_sp<GrTexture> texture(dc->asTexture()); SkASSERT(texture); texture->resourcePriv().setUniqueKey(key); return texture; }
GrTexture* GrClipMaskManager::CreateAlphaClipMask(GrContext* context, int32_t elementsGenID, GrReducedClip::InitialState initialState, const GrReducedClip::ElementList& elements, const SkVector& clipToMaskOffset, const SkIRect& clipSpaceIBounds) { GrResourceProvider* resourceProvider = context->resourceProvider(); GrUniqueKey key; GetClipMaskKey(elementsGenID, clipSpaceIBounds, &key); if (GrTexture* texture = resourceProvider->findAndRefTextureByUniqueKey(key)) { return texture; } // There's no texture in the cache. Let's try to allocate it then. GrSurfaceDesc desc; desc.fWidth = clipSpaceIBounds.width(); desc.fHeight = clipSpaceIBounds.height(); desc.fFlags = kRenderTarget_GrSurfaceFlag; if (context->caps()->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) { desc.fConfig = kAlpha_8_GrPixelConfig; } else { desc.fConfig = kRGBA_8888_GrPixelConfig; } SkAutoTUnref<GrTexture> texture(resourceProvider->createApproxTexture(desc, 0)); if (!texture) { return nullptr; } texture->resourcePriv().setUniqueKey(key); SkAutoTUnref<GrDrawContext> dc(context->drawContext(texture->asRenderTarget())); if (!dc) { return nullptr; } // The texture may be larger than necessary, this rect represents the part of the texture // we populate with a rasterization of the clip. SkIRect maskSpaceIBounds = SkIRect::MakeWH(clipSpaceIBounds.width(), clipSpaceIBounds.height()); // The scratch texture that we are drawing into can be substantially larger than the mask. Only // clear the part that we care about. dc->clear(&maskSpaceIBounds, GrReducedClip::kAllIn_InitialState == initialState ? 0xffffffff : 0x00000000, true); // Set the matrix so that rendered clip elements are transformed to mask space from clip // space. const SkMatrix translate = SkMatrix::MakeTrans(clipToMaskOffset.fX, clipToMaskOffset.fY); // It is important that we use maskSpaceIBounds as the stencil rect in the below loop. // The second pass that zeros the stencil buffer renders the rect maskSpaceIBounds so the first // pass must not set values outside of this bounds or stencil values outside the rect won't be // cleared. // walk through each clip element and perform its set op for (GrReducedClip::ElementList::Iter iter = elements.headIter(); iter.get(); iter.next()) { const Element* element = iter.get(); SkRegion::Op op = element->getOp(); bool invert = element->isInverseFilled(); if (invert || SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) { #ifdef SK_DEBUG GrPathRenderer* pr = GetPathRenderer(context, texture, translate, element); if (Element::kRect_Type != element->getType() && !pr) { // UseSWOnlyPath should now filter out all cases where gpu-side mask merging would // be performed (i.e., pr would be NULL for a non-rect path). // See https://bug.skia.org/4519 for rationale and details. SkASSERT(0); } #endif // draw directly into the result with the stencil set to make the pixels affected // by the clip shape be non-zero. GR_STATIC_CONST_SAME_STENCIL(kStencilInElement, kReplace_StencilOp, kReplace_StencilOp, kAlways_StencilFunc, 0xffff, 0xffff, 0xffff) if (!stencil_element(dc, &maskSpaceIBounds, kStencilInElement, translate, element)) { texture->resourcePriv().removeUniqueKey(); return nullptr; } // Draw to the exterior pixels (those with a zero stencil value). GR_STATIC_CONST_SAME_STENCIL(kDrawOutsideElement, kZero_StencilOp, kZero_StencilOp, kEqual_StencilFunc, 0xffff, 0x0000, 0xffff); if (!dc->drawContextPriv().drawAndStencilRect(&maskSpaceIBounds, kDrawOutsideElement, op, !invert, false, translate, SkRect::Make(clipSpaceIBounds))) { texture->resourcePriv().removeUniqueKey(); return nullptr; } } else {