GrTextContext::GrTextContext(GrContext* context, const GrPaint& paint) : fPaint(paint) { fContext = context; fStrike = NULL; fCurrTexture = NULL; fCurrVertex = 0; const GrClipData* clipData = context->getClip(); GrRect devConservativeBound; clipData->fClipStack->getConservativeBounds( -clipData->fOrigin.fX, -clipData->fOrigin.fY, context->getRenderTarget()->width(), context->getRenderTarget()->height(), &devConservativeBound); devConservativeBound.roundOut(&fClipRect); fAutoMatrix.setIdentity(fContext, &fPaint); fDrawTarget = NULL; fVertices = NULL; fMaxVertices = 0; fVertexLayout = GrDrawTarget::kTextFormat_VertexLayoutBit | GrDrawTarget::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 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; }
/** * getConservativeBounds returns the conservative bounding box of the clip * in device (as opposed to canvas) coordinates. If the bounding box is * the result of purely intersections of rects (with an initial replace) * isIntersectionOfRects will be set to true. */ void GrClipData::getConservativeBounds(const GrSurface* surface, GrIRect* devResult, bool* isIntersectionOfRects) const { GrRect devBounds; fClipStack->getConservativeBounds(-fOrigin.fX, -fOrigin.fY, surface->width(), surface->height(), &devBounds, isIntersectionOfRects); devBounds.roundOut(devResult); }
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); } } }
//////////////////////////////////////////////////////////////////////////////// // Create a 8-bit clip mask in alpha GrTexture* GrClipMaskManager::createAlphaClipMask(int32_t clipStackGenID, InitialState initialState, const ElementList& elements, const SkIRect& clipSpaceIBounds) { GrAssert(kNone_ClipMaskType == fCurrClipMaskType); GrTexture* result; if (this->getMaskTexture(clipStackGenID, clipSpaceIBounds, &result)) { fCurrClipMaskType = kAlpha_ClipMaskType; return result; } if (NULL == result) { fAACache.reset(); return NULL; } GrDrawTarget::AutoGeometryAndStatePush agasp(fGpu, GrDrawTarget::kReset_ASRInit); GrDrawState* drawState = fGpu->drawState(); // The top-left of the mask corresponds to the top-left corner of the bounds. SkVector clipToMaskOffset = { SkIntToScalar(-clipSpaceIBounds.fLeft), SkIntToScalar(-clipSpaceIBounds.fTop) }; // 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()); // We're drawing a coverage mask and want coverage to be run through the blend function. drawState->enableState(GrDrawState::kCoverageDrawing_StateBit); // Set the matrix so that rendered clip elements are transformed to mask space from clip space. drawState->viewMatrix()->setTranslate(clipToMaskOffset); // The scratch texture that we are drawing into can be substantially larger than the mask. Only // clear the part that we care about. fGpu->clear(&maskSpaceIBounds, kAllIn_InitialState == initialState ? 0xffffffff : 0x00000000, result->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. GrDrawTarget::AutoClipRestore acr(fGpu, maskSpaceIBounds); drawState->enableState(GrDrawState::kClip_StateBit); GrAutoScratchTexture temp; // walk through each clip element and perform its set op for (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) { GrPathRenderer* pr = NULL; bool useTemp = !this->canStencilAndDrawElement(result, element, &pr); 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. GrIRect maskSpaceElementIBounds; if (useTemp) { if (invert) { maskSpaceElementIBounds = maskSpaceIBounds; } else { GrRect elementBounds = element->getBounds(); elementBounds.offset(clipToMaskOffset); elementBounds.roundOut(&maskSpaceElementIBounds); } this->getTemp(maskSpaceIBounds.fRight, maskSpaceIBounds.fBottom, &temp); if (NULL == temp.texture()) { fAACache.reset(); return NULL; } dst = temp.texture(); // clear the temp target and set blend to replace fGpu->clear(&maskSpaceElementIBounds, invert ? 0xffffffff : 0x00000000, dst->asRenderTarget()); setup_boolean_blendcoeffs(drawState, SkRegion::kReplace_Op); } else { // draw directly into the result with the stencil set to make the pixels affected // by the clip shape be non-zero. dst = result; GR_STATIC_CONST_SAME_STENCIL(kStencilInElement, kReplace_StencilOp, kReplace_StencilOp, kAlways_StencilFunc, 0xffff, 0xffff, 0xffff); drawState->setStencil(kStencilInElement); setup_boolean_blendcoeffs(drawState, op); } drawState->setAlpha(invert ? 0x00 : 0xff); if (!this->drawElement(dst, element, pr)) { fAACache.reset(); return NULL; } if (useTemp) { // Now draw into the accumulator using the real operation and the temp buffer as a // texture this->mergeMask(result, temp.texture(), op, maskSpaceIBounds, maskSpaceElementIBounds); } else { // Draw to the exterior pixels (those with a zero stencil value). drawState->setAlpha(invert ? 0xff : 0x00); GR_STATIC_CONST_SAME_STENCIL(kDrawOutsideElement, kZero_StencilOp, kZero_StencilOp, kEqual_StencilFunc, 0xffff, 0x0000, 0xffff); drawState->setStencil(kDrawOutsideElement); fGpu->drawSimpleRect(clipSpaceIBounds); drawState->disableStencil(); } } else { // all the remaining ops can just be directly draw into the accumulation buffer drawState->setAlpha(0xff); setup_boolean_blendcoeffs(drawState, op); this->drawElement(result, element); } } fCurrClipMaskType = kAlpha_ClipMaskType; return result; }
//////////////////////////////////////////////////////////////////////////////// // Create a 8-bit clip mask in alpha bool GrClipMaskManager::createAlphaClipMask(const GrClipData& clipDataIn, GrTexture** result, GrIRect *devResultBounds) { GrAssert(NULL != devResultBounds); GrAssert(kNone_ClipMaskType == fCurrClipMaskType); if (this->clipMaskPreamble(clipDataIn, result, devResultBounds)) { fCurrClipMaskType = kAlpha_ClipMaskType; return true; } // Note: 'resultBounds' is in device (as opposed to canvas) coordinates GrTexture* accum = fAACache.getLastMask(); if (NULL == accum) { fAACache.reset(); return false; } GrDrawTarget::AutoStateRestore asr(fGpu, GrDrawTarget::kReset_ASRInit); GrDrawState* drawState = fGpu->drawState(); GrDrawTarget::AutoGeometryPush agp(fGpu); // The mask we generate is translated so that its upper-left corner is at devResultBounds // upper-left corner in device space. GrIRect maskResultBounds = GrIRect::MakeWH(devResultBounds->width(), devResultBounds->height()); // Set the matrix so that rendered clip elements are transformed from the space of the clip // stack to the alpha-mask. This accounts for both translation due to the clip-origin and the // placement of the mask within the device. SkVector clipToMaskOffset = { SkIntToScalar(-devResultBounds->fLeft - clipDataIn.fOrigin.fX), SkIntToScalar(-devResultBounds->fTop - clipDataIn.fOrigin.fY) }; drawState->viewMatrix()->setTranslate(clipToMaskOffset); bool clearToInside; SkRegion::Op firstOp = SkRegion::kReplace_Op; // suppress warning SkClipStack::Iter iter(*clipDataIn.fClipStack, SkClipStack::Iter::kBottom_IterStart); const SkClipStack::Iter::Clip* clip = process_initial_clip_elements(&iter, *devResultBounds, &clearToInside, &firstOp, clipDataIn); // The scratch texture that we are drawing into can be substantially larger than the mask. Only // clear the part that we care about. fGpu->clear(&maskResultBounds, clearToInside ? 0xffffffff : 0x00000000, accum->asRenderTarget()); bool accumClearedToZero = !clearToInside; GrAutoScratchTexture temp; bool first = true; // walk through each clip element and perform its set op for ( ; NULL != clip; clip = iter.nextCombined()) { SkRegion::Op op = clip->fOp; if (first) { first = false; op = firstOp; } if (SkRegion::kReplace_Op == op) { // clear the accumulator and draw the new object directly into it if (!accumClearedToZero) { fGpu->clear(&maskResultBounds, 0x00000000, accum->asRenderTarget()); } setup_boolean_blendcoeffs(drawState, op); this->drawClipShape(accum, clip, *devResultBounds); } else if (SkRegion::kReverseDifference_Op == op || SkRegion::kIntersect_Op == op) { // there is no point in intersecting a screen filling rectangle. if (SkRegion::kIntersect_Op == op && NULL != clip->fRect && contains(*clip->fRect, *devResultBounds, clipDataIn.fOrigin)) { continue; } getTemp(*devResultBounds, &temp); if (NULL == temp.texture()) { fAACache.reset(); return false; } // 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 GrRect elementMaskBounds = clip->getBounds(); elementMaskBounds.offset(clipToMaskOffset); GrIRect elementMaskIBounds; elementMaskBounds.roundOut(&elementMaskIBounds); // clear the temp target & draw into it fGpu->clear(&elementMaskIBounds, 0x00000000, temp.texture()->asRenderTarget()); setup_boolean_blendcoeffs(drawState, SkRegion::kReplace_Op); this->drawClipShape(temp.texture(), clip, elementMaskIBounds); // Now draw into the accumulator using the real operation // and the temp buffer as a texture this->mergeMask(accum, temp.texture(), op, maskResultBounds, elementMaskIBounds); } else { // all the remaining ops can just be directly draw into // the accumulation buffer setup_boolean_blendcoeffs(drawState, op); this->drawClipShape(accum, clip, *devResultBounds); } accumClearedToZero = false; } *result = accum; fCurrClipMaskType = kAlpha_ClipMaskType; return true; }
//////////////////////////////////////////////////////////////////////////////// // sort out what kind of clip mask needs to be created: alpha, stencil, // scissor, or entirely software bool GrClipMaskManager::createClipMask(GrGpu* gpu, const GrClip& clipIn, ScissoringSettings* scissorSettings) { GrAssert(scissorSettings); scissorSettings->fEnableScissoring = false; fClipMaskInStencil = false; fClipMaskInAlpha = false; GrDrawState* drawState = gpu->drawState(); if (!drawState->isClipState()) { return true; } GrRenderTarget* rt = drawState->getRenderTarget(); // GrDrawTarget should have filtered this for us GrAssert(NULL != rt); #if GR_SW_CLIP if (create_mask_in_sw()) { // The clip geometry is complex enough that it will be more // efficient to create it entirely in software GrTexture* result = NULL; GrIRect bound; if (this->createSoftwareClipMask(gpu, clipIn, &result, &bound)) { fClipMaskInAlpha = true; setup_drawstate_aaclip(gpu, result, bound); return true; } } #endif #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 (clipIn.requiresAA() && 0 == rt->numSamples()) { // 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 bound; if (this->createAlphaClipMask(gpu, clipIn, &result, &bound)) { fClipMaskInAlpha = true; setup_drawstate_aaclip(gpu, result, bound); return true; } // if alpha clip mask creation fails fall through to the stencil // buffer method } #endif // GR_AA_CLIP GrRect bounds; GrRect rtRect; rtRect.setLTRB(0, 0, GrIntToScalar(rt->width()), GrIntToScalar(rt->height())); if (clipIn.hasConservativeBounds()) { bounds = clipIn.getConservativeBounds(); if (!bounds.intersect(rtRect)) { bounds.setEmpty(); } } else { bounds = rtRect; } bounds.roundOut(&scissorSettings->fScissorRect); if (scissorSettings->fScissorRect.isEmpty()) { scissorSettings->fScissorRect.setLTRB(0,0,0,0); // TODO: I think we can do an early exit here - after refactoring try: // set fEnableScissoring to true but leave fClipMaskInStencil false // and return - everything is going to be scissored away anyway! } scissorSettings->fEnableScissoring = true; // use the stencil clip if we can't represent the clip as a rectangle. fClipMaskInStencil = !clipIn.isRect() && !clipIn.isEmpty() && !bounds.isEmpty(); if (fClipMaskInStencil) { return this->createStencilClipMask(gpu, clipIn, bounds, scissorSettings); } return true; }
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; // 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; }
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; }
//////////////////////////////////////////////////////////////////////////////// // sort out what kind of clip mask needs to be created: alpha, stencil, // scissor, or entirely software bool GrClipMaskManager::createClipMask(GrGpu* gpu, const GrClip& clipIn, ScissoringSettings* scissorSettings) { GrAssert(scissorSettings); scissorSettings->fEnableScissoring = false; fClipMaskInStencil = false; fClipMaskInAlpha = false; GrDrawState* drawState = gpu->drawState(); if (!drawState->isClipState()) { return true; } GrRenderTarget* rt = drawState->getRenderTarget(); // GrDrawTarget should have filtered this for us GrAssert(NULL != rt); #if GR_SW_CLIP // 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() && useSWOnlyPath(gpu, clipIn)) { // The clip geometry is complex enough that it will be more // efficient to create it entirely in software GrTexture* result = NULL; GrIRect bound; if (this->createSoftwareClipMask(gpu, clipIn, &result, &bound)) { fClipMaskInAlpha = true; setup_drawstate_aaclip(gpu, result, bound); 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() && clipIn.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 bound; if (this->createAlphaClipMask(gpu, clipIn, &result, &bound)) { fClipMaskInAlpha = true; setup_drawstate_aaclip(gpu, result, bound); 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(); GrRect bounds; GrRect rtRect; rtRect.setLTRB(0, 0, GrIntToScalar(rt->width()), GrIntToScalar(rt->height())); if (clipIn.hasConservativeBounds()) { bounds = clipIn.getConservativeBounds(); if (!bounds.intersect(rtRect)) { bounds.setEmpty(); } } else { bounds = rtRect; } bounds.roundOut(&scissorSettings->fScissorRect); if (scissorSettings->fScissorRect.isEmpty()) { scissorSettings->fScissorRect.setLTRB(0,0,0,0); // TODO: I think we can do an early exit here - after refactoring try: // set fEnableScissoring to true but leave fClipMaskInStencil false // and return - everything is going to be scissored away anyway! } scissorSettings->fEnableScissoring = true; // use the stencil clip if we can't represent the clip as a rectangle. fClipMaskInStencil = !clipIn.isRect() && !clipIn.isEmpty() && !bounds.isEmpty(); if (fClipMaskInStencil) { return this->createStencilClipMask(gpu, clipIn, bounds, scissorSettings); } return true; }