void GrDrawTarget::stencilPath(const GrPath* path, GrPathFill fill) { // TODO: extract portions of checkDraw that are relevant to path stenciling. GrAssert(NULL != path); GrAssert(fCaps.pathStencilingSupport()); GrAssert(kHairLine_GrPathFill != fill); GrAssert(!GrIsFillInverted(fill)); this->onStencilPath(path, fill); }
//////////////////////////////////////////////////////////////////////////////// // return true on success; false on failure bool GrSoftwarePathRenderer::onDrawPath(const SkPath& path, GrPathFill fill, const GrVec* translate, GrDrawTarget* target, GrDrawState::StageMask stageMask, bool antiAlias) { if (NULL == fContext) { return false; } GrAutoScratchTexture ast; GrIRect pathBounds, clipBounds; if (!get_path_and_clip_bounds(target, path, translate, &pathBounds, &clipBounds)) { return true; // path is empty so there is nothing to do } if (sw_draw_path_to_mask_texture(path, pathBounds, fill, fContext, translate, &ast, antiAlias)) { GrTexture* texture = ast.texture(); GrAssert(NULL != texture); GrDrawTarget::AutoDeviceCoordDraw adcd(target, stageMask); enum { // the SW path renderer shares this stage with glyph // rendering (kGlyphMaskStage in GrBatchedTextContext) kPathMaskStage = GrPaint::kTotalStages, }; GrAssert(NULL == target->drawState()->getTexture(kPathMaskStage)); target->drawState()->setTexture(kPathMaskStage, texture); target->drawState()->sampler(kPathMaskStage)->reset(); GrScalar w = GrIntToScalar(pathBounds.width()); GrScalar h = GrIntToScalar(pathBounds.height()); GrRect maskRect = GrRect::MakeWH(w / texture->width(), h / texture->height()); const GrRect* srcRects[GrDrawState::kNumStages] = {NULL}; srcRects[kPathMaskStage] = &maskRect; stageMask |= 1 << kPathMaskStage; GrRect dstRect = GrRect::MakeLTRB( SK_Scalar1* pathBounds.fLeft, SK_Scalar1* pathBounds.fTop, SK_Scalar1* pathBounds.fRight, SK_Scalar1* pathBounds.fBottom); target->drawRect(dstRect, NULL, stageMask, srcRects, NULL); target->drawState()->setTexture(kPathMaskStage, NULL); if (GrIsFillInverted(fill)) { draw_around_inv_path(target, stageMask, clipBounds, pathBounds); } return true; } return false; }
bool GrAAConvexPathRenderer::canDrawPath(const SkPath& path, GrPathFill fill, const GrDrawTarget* target, bool antiAlias) const { if (!target->getCaps().shaderDerivativeSupport() || !antiAlias || kHairLine_GrPathFill == fill || GrIsFillInverted(fill) || !path.isConvex()) { return false; } else { return true; } }
bool GrAAConvexPathRenderer::staticCanDrawPath(bool pathIsConvex, GrPathFill fill, const GrDrawTarget* target, bool antiAlias) { if (!target->getCaps().fShaderDerivativeSupport || !antiAlias || kHairLine_PathFill == fill || GrIsFillInverted(fill) || !pathIsConvex) { return false; } else { return true; } }
//////////////////////////////////////////////////////////////////////////////// // return true on success; false on failure bool GrSoftwarePathRenderer::onDrawPath(const SkPath& path, GrPathFill fill, GrDrawTarget* target, bool antiAlias) { if (NULL == fContext) { return false; } GrDrawState* drawState = target->drawState(); GrMatrix vm = drawState->getViewMatrix(); GrIRect devPathBounds, devClipBounds; if (!get_path_and_clip_bounds(target, path, vm, &devPathBounds, &devClipBounds)) { if (GrIsFillInverted(fill)) { draw_around_inv_path(target, devClipBounds, devPathBounds); } return true; } SkAutoTUnref<GrTexture> texture( GrSWMaskHelper::DrawPathMaskToTexture(fContext, path, devPathBounds, fill, antiAlias, &vm)); if (NULL == texture) { return false; } GrSWMaskHelper::DrawToTargetWithPathMask(texture, target, devPathBounds); if (GrIsFillInverted(fill)) { draw_around_inv_path(target, devClipBounds, devPathBounds); } 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); } } }
//////////////////////////////////////////////////////////////////////////////// // Create a 1-bit clip mask in the stencil buffer. 'devClipBounds' are in device // (as opposed to canvas) coordinates bool GrClipMaskManager::createStencilClipMask(const GrClipData& clipDataIn, const GrIRect& devClipBounds) { GrAssert(kNone_ClipMaskType == fCurrClipMaskType); GrDrawState* drawState = fGpu->drawState(); GrAssert(drawState->isClipState()); GrRenderTarget* rt = drawState->getRenderTarget(); GrAssert(NULL != rt); // TODO: dynamically attach a SB when needed. GrStencilBuffer* stencilBuffer = rt->getStencilBuffer(); if (NULL == stencilBuffer) { return false; } if (stencilBuffer->mustRenderClip(clipDataIn, rt->width(), rt->height())) { stencilBuffer->setLastClip(clipDataIn, 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 GrClipData* oldClipData = fGpu->getClip(); // The origin of 'newClipData' is (0, 0) so it is okay to place // a device-coordinate bound in 'newClipStack' SkClipStack newClipStack(devClipBounds); GrClipData newClipData; newClipData.fClipStack = &newClipStack; fGpu->setClip(&newClipData); GrDrawTarget::AutoStateRestore asr(fGpu, GrDrawTarget::kReset_ASRInit); drawState = fGpu->drawState(); drawState->setRenderTarget(rt); GrDrawTarget::AutoGeometryPush agp(fGpu); if (0 != clipDataIn.fOrigin.fX || 0 != clipDataIn.fOrigin.fY) { // Add the saveLayer's offset to the view matrix rather than // offset each individual draw drawState->viewMatrix()->setTranslate( SkIntToScalar(-clipDataIn.fOrigin.fX), SkIntToScalar(-clipDataIn.fOrigin.fY)); } #if !VISUALIZE_COMPLEX_CLIP drawState->enableState(GrDrawState::kNoColorWrites_StateBit); #endif int clipBit = stencilBuffer->bits(); SkASSERT((clipBit <= 16) && "Ganesh only handles 16b or smaller stencil buffers"); clipBit = (1 << (clipBit-1)); GrIRect devRTRect = GrIRect::MakeWH(rt->width(), rt->height()); bool clearToInside; SkRegion::Op firstOp = SkRegion::kReplace_Op; // suppress warning SkClipStack::Iter iter(*oldClipData->fClipStack, SkClipStack::Iter::kBottom_IterStart); const SkClipStack::Iter::Clip* clip = process_initial_clip_elements(&iter, devRTRect, &clearToInside, &firstOp, clipDataIn); fGpu->clearStencilClip(devClipBounds, clearToInside); bool first = true; // walk through each clip element and perform its set op // with the existing clip. for ( ; NULL != clip; clip = iter.nextCombined()) { GrPathFill fill; bool fillInverted = false; // enabled at bottom of loop drawState->disableState(GrGpu::kModifyStencilClip_StateBit); // if the target is MSAA then we want MSAA enabled when the clip is soft if (rt->isMultisampled()) { drawState->setState(GrDrawState::kHWAntialias_StateBit, clip->fDoAA); } // 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? bool canRenderDirectToStencil = false; SkRegion::Op op = clip->fOp; if (first) { first = false; op = firstOp; } GrPathRenderer* pr = NULL; const SkPath* clipPath = NULL; if (NULL != clip->fRect) { canRenderDirectToStencil = true; fill = kEvenOdd_GrPathFill; fillInverted = false; // there is no point in intersecting a screen filling // rectangle. if (SkRegion::kIntersect_Op == op && contains(*clip->fRect, devRTRect, oldClipData->fOrigin)) { continue; } } else { GrAssert(NULL != clip->fPath); fill = get_path_fill(*clip->fPath); fillInverted = GrIsFillInverted(fill); fill = GrNonInvertedFill(fill); clipPath = clip->fPath; pr = this->getContext()->getPathRenderer(*clipPath, fill, fGpu, false, true); if (NULL == pr) { fGpu->setClip(oldClipData); return false; } canRenderDirectToStencil = !pr->requiresStencilPass(*clipPath, fill, fGpu); } 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 (NULL != clip->fRect) { *drawState->stencil() = gDrawToStencil; fGpu->drawSimpleRect(*clip->fRect, NULL); } else { if (canRenderDirectToStencil) { *drawState->stencil() = gDrawToStencil; pr->drawPath(*clipPath, fill, fGpu, false); } else { pr->drawPathToStencil(*clipPath, fill, fGpu); } } } // now we modify the clip bit by rendering either the clip // element directly or a bounding rect of the entire clip. drawState->enableState(GrGpu::kModifyStencilClip_StateBit); for (int p = 0; p < passes; ++p) { *drawState->stencil() = stencilSettings[p]; if (canDrawDirectToClip) { if (NULL != clip->fRect) { SET_RANDOM_COLOR fGpu->drawSimpleRect(*clip->fRect, NULL); } else { SET_RANDOM_COLOR pr->drawPath(*clipPath, fill, fGpu, false); } } else { SET_RANDOM_COLOR // 'devClipBounds' is already in device coordinates so the // translation in the view matrix is inappropriate. // Convert it to canvas space so the drawn rect will // be in the correct location GrRect canvClipBounds; canvClipBounds.set(devClipBounds); device_to_canvas(&canvClipBounds, clipDataIn.fOrigin); fGpu->drawSimpleRect(canvClipBounds, NULL); } } }
bool GrTesselatedPathRenderer::onDrawPath(const SkPath& path, GrPathFill fill, const GrVec* translate, GrDrawTarget* target, GrDrawState::StageMask stageMask, bool antiAlias) { GrDrawTarget::AutoStateRestore asr(target); GrDrawState* drawState = target->drawState(); // face culling doesn't make sense here GrAssert(GrDrawState::kBoth_DrawFace == drawState->getDrawFace()); GrMatrix viewM = drawState->getViewMatrix(); GrScalar tol = GR_Scalar1; tol = GrPathUtils::scaleToleranceToSrc(tol, viewM, path.getBounds()); GrScalar tolSqd = GrMul(tol, tol); int subpathCnt; int maxPts = GrPathUtils::worstCasePointCount(path, &subpathCnt, tol); GrVertexLayout layout = 0; for (int s = 0; s < GrDrawState::kNumStages; ++s) { if ((1 << s) & stageMask) { layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s); } } bool inverted = GrIsFillInverted(fill); if (inverted) { maxPts += 4; subpathCnt++; } if (maxPts > USHRT_MAX) { return false; } SkAutoSTMalloc<8, GrPoint> baseMem(maxPts); GrPoint* base = baseMem; GrPoint* vert = base; GrPoint* subpathBase = base; SkAutoSTMalloc<8, uint16_t> subpathVertCount(subpathCnt); GrPoint pts[4]; SkPath::Iter iter(path, false); bool first = true; int subpath = 0; for (;;) { switch (iter.next(pts)) { case kMove_PathCmd: if (!first) { subpathVertCount[subpath] = vert-subpathBase; subpathBase = vert; ++subpath; } *vert = pts[0]; vert++; break; case kLine_PathCmd: *vert = pts[1]; vert++; break; case kQuadratic_PathCmd: { GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2], tolSqd, &vert, GrPathUtils::quadraticPointCount(pts, tol)); break; } case kCubic_PathCmd: { GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3], tolSqd, &vert, GrPathUtils::cubicPointCount(pts, tol)); break; } case kClose_PathCmd: break; case kEnd_PathCmd: subpathVertCount[subpath] = vert-subpathBase; ++subpath; // this could be only in debug goto FINISHED; } first = false; } FINISHED: if (NULL != translate && 0 != translate->fX && 0 != translate->fY) { for (int i = 0; i < vert - base; i++) { base[i].offset(translate->fX, translate->fY); } } if (inverted) { GrRect bounds; GrAssert(NULL != drawState->getRenderTarget()); bounds.setLTRB(0, 0, GrIntToScalar(drawState->getRenderTarget()->width()), GrIntToScalar(drawState->getRenderTarget()->height())); GrMatrix vmi; if (drawState->getViewInverse(&vmi)) { vmi.mapRect(&bounds); } *vert++ = GrPoint::Make(bounds.fLeft, bounds.fTop); *vert++ = GrPoint::Make(bounds.fLeft, bounds.fBottom); *vert++ = GrPoint::Make(bounds.fRight, bounds.fBottom); *vert++ = GrPoint::Make(bounds.fRight, bounds.fTop); subpathVertCount[subpath++] = 4; } GrAssert(subpath == subpathCnt); GrAssert((vert - base) <= maxPts); size_t count = vert - base; if (count < 3) { return true; } if (subpathCnt == 1 && !inverted && path.isConvex()) { if (antiAlias) { GrEdgeArray edges; GrMatrix inverse, matrix = drawState->getViewMatrix(); drawState->getViewInverse(&inverse); count = computeEdgesAndIntersect(matrix, inverse, base, count, &edges, 0.0f); size_t maxEdges = target->getMaxEdges(); if (count == 0) { return true; } if (count <= maxEdges) { // All edges fit; upload all edges and draw all verts as a fan target->setVertexSourceToArray(layout, base, count); drawState->setEdgeAAData(&edges[0], count); target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count); } else { // Upload "maxEdges" edges and verts at a time, and draw as // separate fans for (size_t i = 0; i < count - 2; i += maxEdges - 2) { edges[i] = edges[0]; base[i] = base[0]; int size = GR_CT_MIN(count - i, maxEdges); target->setVertexSourceToArray(layout, &base[i], size); drawState->setEdgeAAData(&edges[i], size); target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, size); } } drawState->setEdgeAAData(NULL, 0); } else { target->setVertexSourceToArray(layout, base, count); target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count); } return true; } if (antiAlias) { // Run the tesselator once to get the boundaries. GrBoundaryTess btess(count, fill_type_to_glu_winding_rule(fill)); btess.addVertices(base, subpathVertCount, subpathCnt); GrMatrix inverse, matrix = drawState->getViewMatrix(); if (!drawState->getViewInverse(&inverse)) { return false; } if (btess.vertices().count() > USHRT_MAX) { return false; } // Inflate the boundary, and run the tesselator again to generate // interior polys. const GrPointArray& contourPoints = btess.contourPoints(); const GrIndexArray& contours = btess.contours(); GrEdgePolygonTess ptess(contourPoints.count(), GLU_TESS_WINDING_NONZERO, matrix); size_t i = 0; Sk_gluTessBeginPolygon(ptess.tess(), &ptess); for (int contour = 0; contour < contours.count(); ++contour) { int count = contours[contour]; GrEdgeArray edges; int newCount = computeEdgesAndIntersect(matrix, inverse, &btess.contourPoints()[i], count, &edges, 1.0f); Sk_gluTessBeginContour(ptess.tess()); for (int j = 0; j < newCount; j++) { ptess.addVertex(contourPoints[i + j], ptess.vertices().count()); } i += count; Sk_gluTessEndContour(ptess.tess()); } Sk_gluTessEndPolygon(ptess.tess()); if (ptess.vertices().count() > USHRT_MAX) { return false; } // Draw the resulting polys and upload their edge data. drawState->enableState(GrDrawState::kEdgeAAConcave_StateBit); const GrPointArray& vertices = ptess.vertices(); const GrIndexArray& indices = ptess.indices(); const GrDrawState::Edge* edges = ptess.edges(); GR_DEBUGASSERT(indices.count() % 3 == 0); for (int i = 0; i < indices.count(); i += 3) { GrPoint tri_verts[3]; int index0 = indices[i]; int index1 = indices[i + 1]; int index2 = indices[i + 2]; tri_verts[0] = vertices[index0]; tri_verts[1] = vertices[index1]; tri_verts[2] = vertices[index2]; GrDrawState::Edge tri_edges[6]; int t = 0; const GrDrawState::Edge& edge0 = edges[index0 * 2]; const GrDrawState::Edge& edge1 = edges[index0 * 2 + 1]; const GrDrawState::Edge& edge2 = edges[index1 * 2]; const GrDrawState::Edge& edge3 = edges[index1 * 2 + 1]; const GrDrawState::Edge& edge4 = edges[index2 * 2]; const GrDrawState::Edge& edge5 = edges[index2 * 2 + 1]; if (validEdge(edge0) && validEdge(edge1)) { tri_edges[t++] = edge0; tri_edges[t++] = edge1; } if (validEdge(edge2) && validEdge(edge3)) { tri_edges[t++] = edge2; tri_edges[t++] = edge3; } if (validEdge(edge4) && validEdge(edge5)) { tri_edges[t++] = edge4; tri_edges[t++] = edge5; } drawState->setEdgeAAData(&tri_edges[0], t); target->setVertexSourceToArray(layout, &tri_verts[0], 3); target->drawNonIndexed(kTriangles_PrimitiveType, 0, 3); } drawState->setEdgeAAData(NULL, 0); drawState->disableState(GrDrawState::kEdgeAAConcave_StateBit); return true; } GrPolygonTess ptess(count, fill_type_to_glu_winding_rule(fill)); ptess.addVertices(base, subpathVertCount, subpathCnt); const GrPointArray& vertices = ptess.vertices(); const GrIndexArray& indices = ptess.indices(); if (indices.count() > 0) { target->setVertexSourceToArray(layout, vertices.begin(), vertices.count()); target->setIndexSourceToArray(indices.begin(), indices.count()); target->drawIndexed(kTriangles_PrimitiveType, 0, 0, vertices.count(), indices.count()); } return true; }
//////////////////////////////////////////////////////////////////////////////// // Create a 1-bit clip mask in the stencil buffer bool GrClipMaskManager::createStencilClipMask(GrGpu* gpu, const GrClip& clipIn, const GrRect& bounds, ScissoringSettings* scissorSettings) { GrAssert(fClipMaskInStencil); GrDrawState* drawState = gpu->drawState(); GrAssert(drawState->isClipState()); GrRenderTarget* rt = drawState->getRenderTarget(); GrAssert(NULL != rt); // TODO: dynamically attach a SB when needed. GrStencilBuffer* stencilBuffer = rt->getStencilBuffer(); if (NULL == stencilBuffer) { return false; } if (stencilBuffer->mustRenderClip(clipIn, rt->width(), rt->height())) { stencilBuffer->setLastClip(clipIn, 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& clipCopy = stencilBuffer->getLastClip(); gpu->setClip(GrClip(bounds)); GrDrawTarget::AutoStateRestore asr(gpu, GrDrawTarget::kReset_ASRInit); drawState = gpu->drawState(); drawState->setRenderTarget(rt); GrDrawTarget::AutoGeometryPush agp(gpu); gpu->disableScissor(); #if !VISUALIZE_COMPLEX_CLIP drawState->enableState(GrDrawState::kNoColorWrites_StateBit); #endif int count = clipCopy.getElementCount(); int clipBit = stencilBuffer->bits(); SkASSERT((clipBit <= 16) && "Ganesh only handles 16b or smaller stencil buffers"); clipBit = (1 << (clipBit-1)); GrIRect rtRect = GrIRect::MakeWH(rt->width(), rt->height()); bool clearToInside; SkRegion::Op startOp = SkRegion::kReplace_Op; // suppress warning int start = process_initial_clip_elements(clipCopy, rtRect, &clearToInside, &startOp); gpu->clearStencilClip(scissorSettings->fScissorRect, 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(GrGpu::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. SkRegion::Op op = (c == start) ? startOp : clipCopy.getOp(c); GrPathRenderer* pr = NULL; const SkPath* clipPath = NULL; if (kRect_ClipType == clipCopy.getElementType(c)) { canRenderDirectToStencil = true; fill = kEvenOdd_PathFill; fillInverted = false; // there is no point in intersecting a screen filling // rectangle. if (SkRegion::kIntersect_Op == op && contains(clipCopy.getRect(c), rtRect)) { continue; } } else { fill = clipCopy.getPathFill(c); fillInverted = GrIsFillInverted(fill); fill = GrNonInvertedFill(fill); clipPath = &clipCopy.getPath(c); pr = this->getClipPathRenderer(gpu, *clipPath, fill, false); if (NULL == pr) { fClipMaskInStencil = false; gpu->setClip(clipCopy); // restore to the original return false; } canRenderDirectToStencil = !pr->requiresStencilPass(*clipPath, fill, gpu); } 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 == clipCopy.getElementType(c)) { *drawState->stencil() = gDrawToStencil; gpu->drawSimpleRect(clipCopy.getRect(c), NULL, 0); } else { if (canRenderDirectToStencil) { *drawState->stencil() = gDrawToStencil; pr->drawPath(*clipPath, fill, NULL, gpu, 0, false); } else { pr->drawPathToStencil(*clipPath, fill, gpu); } } } // now we modify the clip bit by rendering either the clip // element directly or a bounding rect of the entire clip. drawState->enableState(GrGpu::kModifyStencilClip_StateBit); for (int p = 0; p < passes; ++p) { *drawState->stencil() = stencilSettings[p]; if (canDrawDirectToClip) { if (kRect_ClipType == clipCopy.getElementType(c)) { SET_RANDOM_COLOR gpu->drawSimpleRect(clipCopy.getRect(c), NULL, 0); } else { SET_RANDOM_COLOR pr->drawPath(*clipPath, fill, NULL, gpu, 0, false); } } else { SET_RANDOM_COLOR gpu->drawSimpleRect(bounds, NULL, 0); } } }