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; }
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; }
//////////////////////////////////////////////////////////////////////////////// // 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; }
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); }
// 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); }
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); }
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; }
// 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); }
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(); }
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; } }
//////////////////////////////////////////////////////////////////////////////// // 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; }
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; }
//////////////////////////////////////////////////////////////////////////////// // 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; }
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; }