Пример #1
0
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;
}
Пример #2
0
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();
}
Пример #3
0
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;
}
Пример #4
0
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 {