void GrTesselatedPathRenderer::drawPath(GrDrawTarget::StageBitfield stages) { GrDrawTarget::AutoStateRestore asr(fTarget); // face culling doesn't make sense here GrAssert(GrDrawTarget::kBoth_DrawFace == fTarget->getDrawFace()); GrMatrix viewM = fTarget->getViewMatrix(); GrScalar tol = GR_Scalar1; tol = GrPathUtils::scaleToleranceToSrc(tol, viewM, fPath->getBounds()); GrScalar tolSqd = GrMul(tol, tol); int subpathCnt; int maxPts = GrPathUtils::worstCasePointCount(*fPath, &subpathCnt, tol); GrVertexLayout layout = 0; for (int s = 0; s < GrDrawTarget::kNumStages; ++s) { if ((1 << s) & stages) { layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s); } } bool inverted = IsFillInverted(fFill); if (inverted) { maxPts += 4; subpathCnt++; } if (maxPts > USHRT_MAX) { return; } 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(*fPath, 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 (0 != fTranslate.fX || 0 != fTranslate.fY) { for (int i = 0; i < vert - base; i++) { base[i].offset(fTranslate.fX, fTranslate.fY); } } if (inverted) { GrRect bounds; GrAssert(NULL != fTarget->getRenderTarget()); bounds.setLTRB(0, 0, GrIntToScalar(fTarget->getRenderTarget()->width()), GrIntToScalar(fTarget->getRenderTarget()->height())); GrMatrix vmi; if (fTarget->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; } if (subpathCnt == 1 && !inverted && fPath->isConvex()) { if (fTarget->isAntialiasState()) { GrEdgeArray edges; GrMatrix inverse, matrix = fTarget->getViewMatrix(); fTarget->getViewInverse(&inverse); count = computeEdgesAndIntersect(matrix, inverse, base, count, &edges, 0.0f); size_t maxEdges = fTarget->getMaxEdges(); if (count == 0) { return; } if (count <= maxEdges) { // All edges fit; upload all edges and draw all verts as a fan fTarget->setVertexSourceToArray(layout, base, count); fTarget->setEdgeAAData(&edges[0], count); fTarget->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); fTarget->setVertexSourceToArray(layout, &base[i], size); fTarget->setEdgeAAData(&edges[i], size); fTarget->drawNonIndexed(kTriangleFan_PrimitiveType, 0, size); } } fTarget->setEdgeAAData(NULL, 0); } else { fTarget->setVertexSourceToArray(layout, base, count); fTarget->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count); } return; } if (fTarget->isAntialiasState()) { // Run the tesselator once to get the boundaries. GrBoundaryTess btess(count, fill_type_to_glu_winding_rule(fFill)); btess.addVertices(base, subpathVertCount, subpathCnt); GrMatrix inverse, matrix = fTarget->getViewMatrix(); if (!fTarget->getViewInverse(&inverse)) { return; } if (btess.vertices().count() > USHRT_MAX) { return; } // 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; } // Draw the resulting polys and upload their edge data. fTarget->enableState(GrDrawTarget::kEdgeAAConcave_StateBit); const GrPointArray& vertices = ptess.vertices(); const GrIndexArray& indices = ptess.indices(); const GrDrawTarget::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]; GrDrawTarget::Edge tri_edges[6]; int t = 0; const GrDrawTarget::Edge& edge0 = edges[index0 * 2]; const GrDrawTarget::Edge& edge1 = edges[index0 * 2 + 1]; const GrDrawTarget::Edge& edge2 = edges[index1 * 2]; const GrDrawTarget::Edge& edge3 = edges[index1 * 2 + 1]; const GrDrawTarget::Edge& edge4 = edges[index2 * 2]; const GrDrawTarget::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; } fTarget->setEdgeAAData(&tri_edges[0], t); fTarget->setVertexSourceToArray(layout, &tri_verts[0], 3); fTarget->drawNonIndexed(kTriangles_PrimitiveType, 0, 3); } fTarget->setEdgeAAData(NULL, 0); fTarget->disableState(GrDrawTarget::kEdgeAAConcave_StateBit); return; } GrPolygonTess ptess(count, fill_type_to_glu_winding_rule(fFill)); ptess.addVertices(base, subpathVertCount, subpathCnt); const GrPointArray& vertices = ptess.vertices(); const GrIndexArray& indices = ptess.indices(); if (indices.count() > 0) { fTarget->setVertexSourceToArray(layout, vertices.begin(), vertices.count()); fTarget->setIndexSourceToArray(indices.begin(), indices.count()); fTarget->drawIndexed(kTriangles_PrimitiveType, 0, 0, vertices.count(), indices.count()); } }
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; }