Example #1
0
bool GrSWMaskHelper::init(const GrIRect& resultBounds,
                          const SkMatrix* matrix) {
    if (NULL != matrix) {
        fMatrix = *matrix;
    } else {
        fMatrix.setIdentity();
    }

    // Now translate so the bound's UL corner is at the origin
    fMatrix.postTranslate(-resultBounds.fLeft * SK_Scalar1,
                          -resultBounds.fTop * SK_Scalar1);
    GrIRect bounds = GrIRect::MakeWH(resultBounds.width(),
                                     resultBounds.height());

    fBM.setConfig(SkBitmap::kA8_Config, bounds.fRight, bounds.fBottom);
    if (!fBM.allocPixels()) {
        return false;
    }
    sk_bzero(fBM.getPixels(), fBM.getSafeSize());

    sk_bzero(&fDraw, sizeof(fDraw));
    fRasterClip.setRect(bounds);
    fDraw.fRC    = &fRasterClip;
    fDraw.fClip  = &fRasterClip.bwRgn();
    fDraw.fMatrix = &fMatrix;
    fDraw.fBitmap = &fBM;
    return true;
}
Example #2
0
void GrSWMaskHelper::DrawToTargetWithPathMask(GrTexture* texture,
                                              GrDrawTarget* target,
                                              const GrIRect& rect) {
    GrDrawState* drawState = target->drawState();

    GrDrawState::AutoDeviceCoordDraw adcd(drawState);
    if (!adcd.succeeded()) {
        return;
    }
    enum {
        // the SW path renderer shares this stage with glyph
        // rendering (kGlyphMaskStage in GrBatchedTextContext)
        kPathMaskStage = GrPaint::kTotalStages,
    };
    GrAssert(!drawState->isStageEnabled(kPathMaskStage));
    drawState->stage(kPathMaskStage)->reset();
    drawState->createTextureEffect(kPathMaskStage, texture);
    SkScalar w = SkIntToScalar(rect.width());
    SkScalar h = SkIntToScalar(rect.height());
    GrRect maskRect = GrRect::MakeWH(w / texture->width(),
                                     h / texture->height());

    const GrRect* srcRects[GrDrawState::kNumStages] = { NULL };
    srcRects[kPathMaskStage] = &maskRect;
    GrRect dstRect = GrRect::MakeLTRB(
                            SK_Scalar1 * rect.fLeft,
                            SK_Scalar1 * rect.fTop,
                            SK_Scalar1 * rect.fRight,
                            SK_Scalar1 * rect.fBottom);
    target->drawRect(dstRect, NULL, srcRects, NULL);
    drawState->disableStage(kPathMaskStage);
}
bool GrSWMaskHelper::init(const GrIRect& pathDevBounds, const GrPoint* translate) {
    fMatrix = fContext->getMatrix();
    if (NULL != translate) {
        fMatrix.postTranslate(translate->fX, translate->fY);
    }

    fMatrix.postTranslate(-pathDevBounds.fLeft * SK_Scalar1,
                            -pathDevBounds.fTop * SK_Scalar1);
    GrIRect bounds = GrIRect::MakeWH(pathDevBounds.width(),
                                        pathDevBounds.height());

    fBM.setConfig(SkBitmap::kA8_Config, bounds.fRight, bounds.fBottom);
    if (!fBM.allocPixels()) {
        return false;
    }
    sk_bzero(fBM.getPixels(), fBM.getSafeSize());

    sk_bzero(&fDraw, sizeof(fDraw));
    fRasterClip.setRect(bounds);
    fDraw.fRC    = &fRasterClip;
    fDraw.fClip  = &fRasterClip.bwRgn();
    fDraw.fMatrix = &fMatrix;
    fDraw.fBitmap = &fBM;
    return true;
}
Example #4
0
////////////////////////////////////////////////////////////////////////////////
// Shared preamble between gpu and SW-only AA clip mask creation paths.
// Handles caching, determination of clip mask bound & allocation (if needed)
// of the result texture
// Returns true if there is no more work to be done (i.e., we got a cache hit)
bool GrClipMaskManager::clipMaskPreamble(GrGpu* gpu,
                                         const GrClip& clipIn,
                                         GrTexture** result,
                                         GrIRect *resultBounds) {
    GrDrawState* origDrawState = gpu->drawState();
    GrAssert(origDrawState->isClipState());

    GrRenderTarget* rt = origDrawState->getRenderTarget();
    GrAssert(NULL != rt);

    GrRect rtRect;
    rtRect.setLTRB(0, 0,
                    GrIntToScalar(rt->width()), GrIntToScalar(rt->height()));

    // unlike the stencil path the alpha path is not bound to the size of the
    // render target - determine the minimum size required for the mask
    GrRect bounds;

    if (clipIn.hasConservativeBounds()) {
        bounds = clipIn.getConservativeBounds();
        if (!bounds.intersect(rtRect)) {
            // the mask will be empty in this case
            GrAssert(false);
            bounds.setEmpty();
        }
    } else {
        // still locked to the size of the render target
        bounds = rtRect;
    }

    GrIRect intBounds;
    bounds.roundOut(&intBounds);

    // need to outset a pixel since the standard bounding box computation
    // path doesn't leave any room for antialiasing (esp. w.r.t. rects)
    intBounds.outset(1, 1);

    // TODO: make sure we don't outset if bounds are still 0,0 @ min

    if (fAACache.canReuse(clipIn, 
                          intBounds.width(),
                          intBounds.height())) {
        *result = fAACache.getLastMask();
        fAACache.getLastBound(resultBounds);
        return true;
    }

    this->setupCache(clipIn, intBounds);

    *resultBounds = intBounds;
    return false;
}
Example #5
0
void GrClipMaskManager::setupCache(const SkClipStack& clipIn,
                                   const GrIRect& bounds) {
    // Since we are setting up the cache we know the last lookup was a miss
    // Free up the currently cached mask so it can be reused
    fAACache.reset();

    GrTextureDesc desc;
    desc.fFlags = kRenderTarget_GrTextureFlagBit|kNoStencil_GrTextureFlagBit;
    desc.fWidth = bounds.width();
    desc.fHeight = bounds.height();
    desc.fConfig = kAlpha_8_GrPixelConfig;

    fAACache.acquireMask(clipIn, desc, bounds);
}
Example #6
0
// get a texture to act as a temporary buffer for AA clip boolean operations
// TODO: given the expense of createTexture we may want to just cache this too
void GrClipMaskManager::getTemp(const GrIRect& bounds,
                                GrAutoScratchTexture* temp) {
    if (NULL != temp->texture()) {
        // we've already allocated the temp texture
        return;
    }

    GrTextureDesc desc;
    desc.fFlags = kRenderTarget_GrTextureFlagBit|kNoStencil_GrTextureFlagBit;
    desc.fWidth = bounds.width();
    desc.fHeight = bounds.height();
    desc.fConfig = kAlpha_8_GrPixelConfig;

    temp->set(this->getContext(), desc);
}
Example #7
0
void GrClipMaskManager::setupCache(const GrClip& clipIn,
                                   const GrIRect& bounds) {
    // Since we are setting up the cache we know the last lookup was a miss
    // Free up the currently cached mask so it can be reused
    fAACache.reset();

    const GrTextureDesc desc = {
        kRenderTarget_GrTextureFlagBit|kNoStencil_GrTextureFlagBit,
        bounds.width(),
        bounds.height(),
        kAlpha_8_GrPixelConfig,
        0           // samples
    };

    fAACache.acquireMask(clipIn, desc, bounds);
}
Example #8
0
void GrInOrderDrawBuffer::clear(const GrIRect* rect, GrColor color) {
    GrIRect r;
    if (NULL == rect) {
        // We could do something smart and remove previous draws and clears to
        // the current render target. If we get that smart we have to make sure
        // those draws aren't read before this clear (render-to-texture).
        r.setLTRB(0, 0, 
                  this->getRenderTarget()->width(), 
                  this->getRenderTarget()->height());
        rect = &r;
    }
    Clear& clr = fClears.push_back();
    clr.fColor = color;
    clr.fBeforeDrawIdx = fDraws.count();
    clr.fRect = *rect;
}
Example #9
0
// get a texture to act as a temporary buffer for AA clip boolean operations
// TODO: given the expense of createTexture we may want to just cache this too
void GrClipMaskManager::getTemp(const GrIRect& bounds, 
                                GrAutoScratchTexture* temp) {
    if (NULL != temp->texture()) {
        // we've already allocated the temp texture
        return;
    }

    const GrTextureDesc desc = {
        kRenderTarget_GrTextureFlagBit|kNoStencil_GrTextureFlagBit,
        bounds.width(),
        bounds.height(),
        kAlpha_8_GrPixelConfig,
        0           // samples
    };

    temp->set(fAACache.getContext(), desc);
}
Example #10
0
void GrInOrderDrawBuffer::clear(const GrIRect* rect, GrColor color, GrRenderTarget* renderTarget) {
    GrIRect r;
    if (NULL == renderTarget) {
        renderTarget = this->drawState()->getRenderTarget();
        GrAssert(NULL != renderTarget);
    }
    if (NULL == rect) {
        // We could do something smart and remove previous draws and clears to
        // the current render target. If we get that smart we have to make sure
        // those draws aren't read before this clear (render-to-texture).
        r.setLTRB(0, 0, renderTarget->width(), renderTarget->height());
        rect = &r;
    }
    Clear* clr = this->recordClear();
    clr->fColor = color;
    clr->fRect = *rect;
    clr->fRenderTarget = renderTarget;
    renderTarget->ref();
}
Example #11
0
void GrClip::setFromIRect(const GrIRect& r) {
    fList.reset();
    if (r.isEmpty()) {
        // use a canonical empty rect for == testing.
        setEmpty();
    } else {
        fList.push_back();
        fList.back().fRect.set(r);
        fList.back().fType = kRect_ClipType;
        fConservativeBounds.set(r);
        fConservativeBoundsValid = true;
    }
}
Example #12
0
////////////////////////////////////////////////////////////////////////////////
// sort out what kind of clip mask needs to be created: alpha, stencil,
// scissor, or entirely software
bool GrClipMaskManager::setupClipping(const GrClipData* clipDataIn) {
    fCurrClipMaskType = kNone_ClipMaskType;

    GrDrawState* drawState = fGpu->drawState();
    if (!drawState->isClipState() || clipDataIn->fClipStack->isWideOpen()) {
        fGpu->disableScissor();
        this->setGpuStencil();
        return true;
    }

    GrRenderTarget* rt = drawState->getRenderTarget();
    // GrDrawTarget should have filtered this for us
    GrAssert(NULL != rt);

    GrIRect devClipBounds;
    bool isIntersectionOfRects = false;

    clipDataIn->getConservativeBounds(rt, &devClipBounds, &isIntersectionOfRects);
    if (devClipBounds.isEmpty()) {
        return false;
    }

#if GR_SW_CLIP
    bool requiresAA = requires_AA(*clipDataIn->fClipStack);

    // If MSAA is enabled we can do everything in the stencil buffer.
    // Otherwise check if we should just create the entire clip mask
    // in software (this will only happen if the clip mask is anti-aliased
    // and too complex for the gpu to handle in its entirety)
    if (0 == rt->numSamples() &&
        requiresAA &&
        this->useSWOnlyPath(*clipDataIn->fClipStack)) {
        // The clip geometry is complex enough that it will be more
        // efficient to create it entirely in software
        GrTexture* result = NULL;
        GrIRect devBound;
        if (this->createSoftwareClipMask(*clipDataIn, &result, &devBound)) {
            setup_drawstate_aaclip(fGpu, result, devBound);
            fGpu->disableScissor();
            this->setGpuStencil();
            return true;
        }

        // if SW clip mask creation fails fall through to the other
        // two possible methods (bottoming out at stencil clipping)
    }
#endif // GR_SW_CLIP

#if GR_AA_CLIP
    // If MSAA is enabled use the (faster) stencil path for AA clipping
    // otherwise the alpha clip mask is our only option
    if (0 == rt->numSamples() && requiresAA) {
        // Since we are going to create a destination texture of the correct
        // size for the mask (rather than being bound by the size of the
        // render target) we aren't going to use scissoring like the stencil
        // path does (see scissorSettings below)
        GrTexture* result = NULL;
        GrIRect devBound;
        if (this->createAlphaClipMask(*clipDataIn, &result, &devBound)) {
            setup_drawstate_aaclip(fGpu, result, devBound);
            fGpu->disableScissor();
            this->setGpuStencil();
            return true;
        }

        // if alpha clip mask creation fails fall through to the stencil
        // buffer method
    }
#endif // GR_AA_CLIP

    // Either a hard (stencil buffer) clip was explicitly requested or
    // an antialiased clip couldn't be created. In either case, free up
    // the texture in the antialiased mask cache.
    // TODO: this may require more investigation. Ganesh performs a lot of
    // utility draws (e.g., clears, InOrderDrawBuffer playbacks) that hit
    // the stencil buffer path. These may be "incorrectly" clearing the
    // AA cache.
    fAACache.reset();

    // If the clip is a rectangle then just set the scissor. Otherwise, create
    // a stencil mask.
    if (isIntersectionOfRects) {
        fGpu->enableScissor(devClipBounds);
        this->setGpuStencil();
        return true;
    }

    // use the stencil clip if we can't represent the clip as a rectangle.
    bool useStencil = !clipDataIn->fClipStack->isWideOpen() &&
                      !devClipBounds.isEmpty();

    if (useStencil) {
        this->createStencilClipMask(*clipDataIn, devClipBounds);
    }
    // This must occur after createStencilClipMask. That function may change
    // the scissor. Also, it only guarantees that the stencil mask is correct
    // within the bounds it was passed, so we must use both stencil and scissor
    // test to the bounds for the final draw.
    fGpu->enableScissor(devClipBounds);
    this->setGpuStencil();
    return true;
}
Example #13
0
bool GrGpu::setupClipAndFlushState(GrPrimitiveType type) {
    const GrIRect* r = NULL;
    GrIRect clipRect;

    // we check this early because we need a valid
    // render target to setup stencil clipping
    // before even going into flushGraphicsState
    if (NULL == fCurrDrawState.fRenderTarget) {
        GrAssert(!"No render target bound.");
        return false;
    }

    if (fCurrDrawState.fFlagBits & kClip_StateBit) {
        GrRenderTarget& rt = *fCurrDrawState.fRenderTarget;

        GrRect bounds;
        GrRect rtRect;
        rtRect.setLTRB(0, 0,
                       GrIntToScalar(rt.width()), GrIntToScalar(rt.height()));
        if (fClip.hasConservativeBounds()) {
            bounds = fClip.getConservativeBounds();
            bounds.intersectWith(rtRect);
        } else {
            bounds = rtRect;
        }

        bounds.roundOut(&clipRect);
        if  (clipRect.isEmpty()) {
            clipRect.setLTRB(0,0,0,0);
        }
        r = &clipRect;

        fClipState.fClipInStencil = !fClip.isRect() &&
                                    !fClip.isEmpty() &&
                                    !bounds.isEmpty();

        if (fClipState.fClipInStencil &&
            (fClipState.fClipIsDirty ||
             fClip != rt.fLastStencilClip)) {

            rt.fLastStencilClip = fClip;
            // we set the current clip to the bounds so that our recursive
            // draws are scissored to them. We use the copy of the complex clip
            // in the rt to render
            const GrClip& clip = rt.fLastStencilClip;
            fClip.setFromRect(bounds);

            AutoStateRestore asr(this);
            AutoInternalDrawGeomRestore aidgr(this);

            this->setViewMatrix(GrMatrix::I());
            this->eraseStencilClip(clipRect);
            this->flushScissor(NULL);
#if !VISUALIZE_COMPLEX_CLIP
            this->enableState(kNoColorWrites_StateBit);
#else
            this->disableState(kNoColorWrites_StateBit);
#endif
            int count = clip.getElementCount();
            int clipBit = rt.stencilBits();
            clipBit = (1 << (clipBit-1));

            // often we'll see the first two elements of the clip are
            // the full rt size and another element intersected with it.
            // We can skip the first full-size rect and save a big rect draw.
            int firstElement = 0;
            if (clip.getElementCount() > 1 &&
                kRect_ClipType == clip.getElementType(0) &&
                kIntersect_SetOp == clip.getOp(1)&&
                clip.getRect(0).contains(bounds)) {
                firstElement = 1;
            }

            // walk through each clip element and perform its set op
            // with the existing clip.
            for (int c = firstElement; c < count; ++c) {
                GrPathFill fill;
                // enabled at bottom of loop
                this->disableState(kModifyStencilClip_StateBit);

                bool canDrawDirectToClip;
                if (kRect_ClipType == clip.getElementType(c)) {
                    canDrawDirectToClip = true;
                    fill = kEvenOdd_PathFill;
                } else {
                    fill = clip.getPathFill(c);
                    GrPathRenderer* pr = this->getPathRenderer();
                    canDrawDirectToClip = pr->requiresStencilPass(this, clip.getPath(c), fill);
                }

                GrSetOp op = firstElement == c ? kReplace_SetOp : clip.getOp(c);
                int passes;
                GrStencilSettings stencilSettings[GrStencilSettings::kMaxStencilClipPasses];

                canDrawDirectToClip = GrStencilSettings::GetClipPasses(op, canDrawDirectToClip,
                                                                       clipBit, IsFillInverted(fill),
                                                                       &passes, stencilSettings);

                // draw the element to the client stencil bits if necessary
                if (!canDrawDirectToClip) {
                    if (kRect_ClipType == clip.getElementType(c)) {
                        static const GrStencilSettings gDrawToStencil = {
                            kIncClamp_StencilOp, kIncClamp_StencilOp,
                            kIncClamp_StencilOp, kIncClamp_StencilOp,
                            kAlways_StencilFunc, kAlways_StencilFunc,
                            0xffffffff,          0xffffffff,
                            0x00000000,          0x00000000,
                            0xffffffff,          0xffffffff,
                        };
                        this->setStencil(gDrawToStencil);
                        SET_RANDOM_COLOR
                        this->drawSimpleRect(clip.getRect(c), NULL, 0);
                    } else {
                        SET_RANDOM_COLOR
                        getPathRenderer()->drawPathToStencil(this, clip.getPath(c),
                                                             NonInvertedFill(fill),
                                                             NULL);
                    }
                }

                // now we modify the clip bit by rendering either the clip
                // element directly or a bounding rect of the entire clip.
                this->enableState(kModifyStencilClip_StateBit);
                for (int p = 0; p < passes; ++p) {
                    this->setStencil(stencilSettings[p]);
                    if (canDrawDirectToClip) {
                        if (kRect_ClipType == clip.getElementType(c)) {
                            SET_RANDOM_COLOR
                            this->drawSimpleRect(clip.getRect(c), NULL, 0);
                        } else {
                            SET_RANDOM_COLOR
                            getPathRenderer()->drawPath(this, 0,
                                                        clip.getPath(c),
                                                        fill, NULL);
                        }
                    } else {
                        SET_RANDOM_COLOR
                        this->drawSimpleRect(bounds, 0, NULL);
                    }
                }
            }
            fClip = clip;
            // recusive draws would have disabled this.
            fClipState.fClipInStencil = true;
        }

        fClipState.fClipIsDirty = false;
    }

    // Must flush the scissor after graphics state
    if (!this->flushGraphicsState(type)) {
        return false;
    }
    this->flushScissor(r);
    return true;
}
Example #14
0
////////////////////////////////////////////////////////////////////////////////
// basic test of the cache's base functionality:
//  push, pop, set, canReuse & getters
static void test_cache(skiatest::Reporter* reporter, GrContext* context) {

    if (false) { // avoid bit rot, suppress warning
        createTexture(context);
    }
    GrClipMaskCache cache;

    cache.setContext(context);

    SkClipStack emptyClip;
    emptyClip.reset();

    GrIRect emptyBound;
    emptyBound.setEmpty();

    // check initial state
    check_state(reporter, cache, emptyClip, NULL, emptyBound);

    // set the current state
    GrIRect bound1;
    bound1.set(0, 0, 100, 100);

    SkClipStack clip1(bound1);

    GrTextureDesc desc;
    desc.fFlags = kRenderTarget_GrTextureFlagBit;
    desc.fWidth = X_SIZE;
    desc.fHeight = Y_SIZE;
    desc.fConfig = kSkia8888_PM_GrPixelConfig;

    cache.acquireMask(clip1, desc, bound1);

    GrTexture* texture1 = cache.getLastMask();
    REPORTER_ASSERT(reporter, texture1);
    if (NULL == texture1) {
        return;
    }

    // check that the set took
    check_state(reporter, cache, clip1, texture1, bound1);
    REPORTER_ASSERT(reporter, 1 == texture1->getRefCnt());

    // push the state
    cache.push();

    // verify that the pushed state is initially empty
    check_state(reporter, cache, emptyClip, NULL, emptyBound);
    REPORTER_ASSERT(reporter, 1 == texture1->getRefCnt());

    // modify the new state
    GrIRect bound2;
    bound2.set(-10, -10, 10, 10);

    SkClipStack clip2(bound2);

    cache.acquireMask(clip2, desc, bound2);

    GrTexture* texture2 = cache.getLastMask();
    REPORTER_ASSERT(reporter, texture2);
    if (NULL == texture2) {
        return;
    }

    // check that the changes took
    check_state(reporter, cache, clip2, texture2, bound2);
    REPORTER_ASSERT(reporter, 1 == texture1->getRefCnt());
    REPORTER_ASSERT(reporter, 1 == texture2->getRefCnt());

    // check to make sure canReuse works
    REPORTER_ASSERT(reporter, cache.canReuse(clip2, bound2));
    REPORTER_ASSERT(reporter, !cache.canReuse(clip1, bound1));

    // pop the state
    cache.pop();

    // verify that the old state is restored
    check_state(reporter, cache, clip1, texture1, bound1);
    REPORTER_ASSERT(reporter, 1 == texture1->getRefCnt());
    REPORTER_ASSERT(reporter, 1 == texture2->getRefCnt());

    // manually clear the state
    cache.reset();

    // verify it is now empty
    check_state(reporter, cache, emptyClip, NULL, emptyBound);
    REPORTER_ASSERT(reporter, 1 == texture1->getRefCnt());
    REPORTER_ASSERT(reporter, 1 == texture2->getRefCnt());

    // pop again - so there is no state
    cache.pop();

#if !defined(SK_DEBUG)
    // verify that the getters don't crash
    // only do in release since it generates asserts in debug
    check_state(reporter, cache, emptyClip, NULL, emptyBound);
#endif
    REPORTER_ASSERT(reporter, 1 == texture1->getRefCnt());
    REPORTER_ASSERT(reporter, 1 == texture2->getRefCnt());
}
bool GrAAHairLinePathRenderer::createGeom(GrDrawTarget::StageBitfield stages) {

    int rtHeight = fTarget->getRenderTarget()->height();

    GrIRect clip;
    if (fTarget->getClip().hasConservativeBounds()) {
        GrRect clipRect =  fTarget->getClip().getConservativeBounds();
        clipRect.roundOut(&clip);
    } else {
        clip.setLargest();
    }

    // If none of the inputs that affect generation of path geometry have
    // have changed since last previous path draw then we can reuse the
    // previous geoemtry.
    if (stages == fPreviousStages &&
        fPreviousViewMatrix == fTarget->getViewMatrix() &&
        fPreviousTranslate == fTranslate &&
        rtHeight == fPreviousRTHeight &&
        fClipRect == clip) {
        return true;
    }

    GrVertexLayout layout = GrDrawTarget::kEdge_VertexLayoutBit;
    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
        if ((1 << s) & stages) {
            layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
        }
    }

    GrMatrix viewM = fTarget->getViewMatrix();

    PREALLOC_PTARRAY(128) lines;
    PREALLOC_PTARRAY(128) quads;
    IntArray qSubdivs;
    fQuadCnt = generate_lines_and_quads(*fPath, viewM, fTranslate, clip,
                                        &lines, &quads, &qSubdivs);

    fLineSegmentCnt = lines.count() / 2;
    int vertCnt = kVertsPerLineSeg * fLineSegmentCnt + kVertsPerQuad * fQuadCnt;

    GrAssert(sizeof(Vertex) == GrDrawTarget::VertexSize(layout));

    Vertex* verts;
    if (!fTarget->reserveVertexSpace(layout, vertCnt, (void**)&verts)) {
        return false;
    }
    Vertex* base = verts;

    const GrMatrix* toDevice = NULL;
    const GrMatrix* toSrc = NULL;
    GrMatrix ivm;

    if (viewM.hasPerspective()) {
        if (viewM.invert(&ivm)) {
            toDevice = &viewM;
            toSrc = &ivm;
        }
    }

    for (int i = 0; i < fLineSegmentCnt; ++i) {
        add_line(&lines[2*i], rtHeight, toSrc, &verts);
    }

    int unsubdivQuadCnt = quads.count() / 3;
    for (int i = 0; i < unsubdivQuadCnt; ++i) {
        GrAssert(qSubdivs[i] >= 0);
        add_quads(&quads[3*i], qSubdivs[i], toDevice, toSrc, &verts);
    }

    fPreviousStages = stages;
    fPreviousViewMatrix = fTarget->getViewMatrix();
    fPreviousRTHeight = rtHeight;
    fClipRect = clip;
    fPreviousTranslate = fTranslate;
    return true;
}
Example #16
0
bool GrGpu::setupClipAndFlushState(GrPrimitiveType type) {
    const GrIRect* r = NULL;
    GrIRect clipRect;

    GrDrawState* drawState = this->drawState();
    const GrRenderTarget* rt = drawState->getRenderTarget();

    // GrDrawTarget should have filtered this for us
    GrAssert(NULL != rt);

    if (drawState->isClipState()) {

        GrRect bounds;
        GrRect rtRect;
        rtRect.setLTRB(0, 0,
                       GrIntToScalar(rt->width()), GrIntToScalar(rt->height()));
        if (fClip.hasConservativeBounds()) {
            bounds = fClip.getConservativeBounds();
            if (!bounds.intersect(rtRect)) {
                bounds.setEmpty();
            }
        } else {
            bounds = rtRect;
        }

        bounds.roundOut(&clipRect);
        if  (clipRect.isEmpty()) {
            clipRect.setLTRB(0,0,0,0);
        }
        r = &clipRect;

        // use the stencil clip if we can't represent the clip as a rectangle.
        fClipInStencil = !fClip.isRect() && !fClip.isEmpty() && 
                         !bounds.isEmpty();

        // TODO: dynamically attach a SB when needed.
        GrStencilBuffer* stencilBuffer = rt->getStencilBuffer();
        if (fClipInStencil && NULL == stencilBuffer) {
            return false;
        }

        if (fClipInStencil &&
            stencilBuffer->mustRenderClip(fClip, rt->width(), rt->height())) {

            stencilBuffer->setLastClip(fClip, rt->width(), rt->height());

            // we set the current clip to the bounds so that our recursive
            // draws are scissored to them. We use the copy of the complex clip
            // we just stashed on the SB to render from. We set it back after
            // we finish drawing it into the stencil.
            const GrClip& clip = stencilBuffer->getLastClip();
            fClip.setFromRect(bounds);

            AutoStateRestore asr(this);
            AutoGeometryPush agp(this);

            drawState->setViewMatrix(GrMatrix::I());
            this->flushScissor(NULL);
#if !VISUALIZE_COMPLEX_CLIP
            drawState->enableState(GrDrawState::kNoColorWrites_StateBit);
#else
            drawState->disableState(GrDrawState::kNoColorWrites_StateBit);
#endif
            int count = clip.getElementCount();
            int clipBit = stencilBuffer->bits();
            SkASSERT((clipBit <= 16) &&
                     "Ganesh only handles 16b or smaller stencil buffers");
            clipBit = (1 << (clipBit-1));
            
            bool clearToInside;
            GrSetOp startOp = kReplace_SetOp; // suppress warning
            int start = process_initial_clip_elements(clip,
                                                      rtRect,
                                                      &clearToInside,
                                                      &startOp);

            this->clearStencilClip(clipRect, clearToInside);

            // walk through each clip element and perform its set op
            // with the existing clip.
            for (int c = start; c < count; ++c) {
                GrPathFill fill;
                bool fillInverted;
                // enabled at bottom of loop
                drawState->disableState(kModifyStencilClip_StateBit);

                bool canRenderDirectToStencil; // can the clip element be drawn
                                               // directly to the stencil buffer
                                               // with a non-inverted fill rule
                                               // without extra passes to
                                               // resolve in/out status.

                GrPathRenderer* pr = NULL;
                const GrPath* clipPath = NULL;
                GrPathRenderer::AutoClearPath arp;
                if (kRect_ClipType == clip.getElementType(c)) {
                    canRenderDirectToStencil = true;
                    fill = kEvenOdd_PathFill;
                    fillInverted = false;
                    // there is no point in intersecting a screen filling
                    // rectangle.
                    if (kIntersect_SetOp == clip.getOp(c) &&
                        clip.getRect(c).contains(rtRect)) {
                        continue;
                    }
                } else {
                    fill = clip.getPathFill(c);
                    fillInverted = GrIsFillInverted(fill);
                    fill = GrNonInvertedFill(fill);
                    clipPath = &clip.getPath(c);
                    pr = this->getClipPathRenderer(*clipPath, fill);
                    if (NULL == pr) {
                        fClipInStencil = false;
                        fClip = clip;
                        return false;
                    }
                    canRenderDirectToStencil =
                        !pr->requiresStencilPass(this, *clipPath, fill);
                    arp.set(pr, this, clipPath, fill, false, NULL);
                }

                GrSetOp op = (c == start) ? startOp : clip.getOp(c);
                int passes;
                GrStencilSettings stencilSettings[GrStencilSettings::kMaxStencilClipPasses];

                bool canDrawDirectToClip; // Given the renderer, the element,
                                          // fill rule, and set operation can
                                          // we render the element directly to
                                          // stencil bit used for clipping.
                canDrawDirectToClip =
                    GrStencilSettings::GetClipPasses(op,
                                                     canRenderDirectToStencil,
                                                     clipBit,
                                                     fillInverted,
                                                     &passes, stencilSettings);

                // draw the element to the client stencil bits if necessary
                if (!canDrawDirectToClip) {
                    GR_STATIC_CONST_SAME_STENCIL(gDrawToStencil,
                        kIncClamp_StencilOp,
                        kIncClamp_StencilOp,
                        kAlways_StencilFunc,
                        0xffff,
                        0x0000,
                        0xffff);
                    SET_RANDOM_COLOR
                    if (kRect_ClipType == clip.getElementType(c)) {
                        *drawState->stencil() = gDrawToStencil;
                        this->drawSimpleRect(clip.getRect(c), NULL, 0);
                    } else {
                        if (canRenderDirectToStencil) {
                            *drawState->stencil() = gDrawToStencil;
                            pr->drawPath(0);
                        } else {
                            pr->drawPathToStencil();
                        }
                    }
                }

                // now we modify the clip bit by rendering either the clip
                // element directly or a bounding rect of the entire clip.
                drawState->enableState(kModifyStencilClip_StateBit);
                for (int p = 0; p < passes; ++p) {
                    *drawState->stencil() = stencilSettings[p];
                    if (canDrawDirectToClip) {
                        if (kRect_ClipType == clip.getElementType(c)) {
                            SET_RANDOM_COLOR
                            this->drawSimpleRect(clip.getRect(c), NULL, 0);
                        } else {
                            SET_RANDOM_COLOR
                            pr->drawPath(0);
                        }
                    } else {
                        SET_RANDOM_COLOR
                        this->drawSimpleRect(bounds, NULL, 0);
                    }
                }
            }
bool GrAAHairLinePathRenderer::createGeom(
            const SkPath& path,
            const GrVec* translate,
            GrDrawTarget* target,
            GrDrawState::StageMask stageMask,
            int* lineCnt,
            int* quadCnt,
            GrDrawTarget::AutoReleaseGeometry* arg) {
    const GrDrawState& drawState = target->getDrawState();
    int rtHeight = drawState.getRenderTarget()->height();

    GrIRect clip;
    if (target->getClip().hasConservativeBounds()) {
        GrRect clipRect =  target->getClip().getConservativeBounds();
        clipRect.roundOut(&clip);
    } else {
        clip.setLargest();
    }


    GrVertexLayout layout = GrDrawTarget::kEdge_VertexLayoutBit;
    GrMatrix viewM = drawState.getViewMatrix();

    PREALLOC_PTARRAY(128) lines;
    PREALLOC_PTARRAY(128) quads;
    IntArray qSubdivs;
    static const GrVec gZeroVec = {0, 0};
    if (NULL == translate) {
        translate = &gZeroVec;
    }
    *quadCnt = generate_lines_and_quads(path, viewM, *translate, clip,
                                        &lines, &quads, &qSubdivs);

    *lineCnt = lines.count() / 2;
    int vertCnt = kVertsPerLineSeg * *lineCnt + kVertsPerQuad * *quadCnt;

    GrAssert(sizeof(Vertex) == GrDrawTarget::VertexSize(layout));

    if (!arg->set(target, layout, vertCnt, 0)) {
        return false;
    }

    Vertex* verts = reinterpret_cast<Vertex*>(arg->vertices());

    const GrMatrix* toDevice = NULL;
    const GrMatrix* toSrc = NULL;
    GrMatrix ivm;

    if (viewM.hasPerspective()) {
        if (viewM.invert(&ivm)) {
            toDevice = &viewM;
            toSrc = &ivm;
        }
    }

    for (int i = 0; i < *lineCnt; ++i) {
        add_line(&lines[2*i], rtHeight, toSrc, &verts);
    }

    int unsubdivQuadCnt = quads.count() / 3;
    for (int i = 0; i < unsubdivQuadCnt; ++i) {
        GrAssert(qSubdivs[i] >= 0);
        add_quads(&quads[3*i], qSubdivs[i], toDevice, toSrc, &verts);
    }

    return true;
}