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); }
//////////////////////////////////////////////////////////////////////////////// // 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; }
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 GrTextContext) // && edge rendering (kEdgeEffectStage in GrContext) kPathMaskStage = GrPaint::kTotalStages, }; GrRect dstRect = GrRect::MakeLTRB( SK_Scalar1 * rect.fLeft, SK_Scalar1 * rect.fTop, SK_Scalar1 * rect.fRight, SK_Scalar1 * rect.fBottom); // We want to use device coords to compute the texture coordinates. We set our matrix to be // equal to the view matrix followed by a translation so that the top-left of the device bounds // maps to 0,0, and then a scaling matrix to normalized coords. We apply this matrix to the // vertex positions rather than local coords. SkMatrix maskMatrix; maskMatrix.setIDiv(texture->width(), texture->height()); maskMatrix.preTranslate(SkIntToScalar(-rect.fLeft), SkIntToScalar(-rect.fTop)); maskMatrix.preConcat(drawState->getViewMatrix()); GrAssert(!drawState->isStageEnabled(kPathMaskStage)); drawState->setEffect(kPathMaskStage, GrSimpleTextureEffect::Create(texture, maskMatrix, false, GrEffect::kPosition_CoordsType))->unref(); target->drawSimpleRect(dstRect); drawState->disableStage(kPathMaskStage); }
bool GrAAConvexPathRenderer::onDrawPath(const SkPath& origPath, GrPathFill fill, GrDrawTarget* target, bool antiAlias) { const SkPath* path = &origPath; if (path->isEmpty()) { return true; } GrDrawState* drawState = target->drawState(); GrDrawState::AutoDeviceCoordDraw adcd(drawState); if (!adcd.succeeded()) { return false; } const GrMatrix* vm = &adcd.getOriginalMatrix(); GrVertexLayout layout = 0; layout |= GrDrawTarget::kEdge_VertexLayoutBit; // We use the fact that SkPath::transform path does subdivision based on // perspective. Otherwise, we apply the view matrix when copying to the // segment representation. SkPath tmpPath; if (vm->hasPerspective()) { origPath.transform(*vm, &tmpPath); path = &tmpPath; vm = &GrMatrix::I(); } QuadVertex *verts; uint16_t* idxs; int vCount; int iCount; enum { kPreallocSegmentCnt = 512 / sizeof(Segment), }; SkSTArray<kPreallocSegmentCnt, Segment, true> segments; SkPoint fanPt; if (!get_segments(*path, *vm, &segments, &fanPt, &vCount, &iCount)) { return false; } GrDrawTarget::AutoReleaseGeometry arg(target, layout, vCount, iCount); if (!arg.succeeded()) { return false; } verts = reinterpret_cast<QuadVertex*>(arg.vertices()); idxs = reinterpret_cast<uint16_t*>(arg.indices()); create_vertices(segments, fanPt, verts, idxs); GrDrawState::VertexEdgeType oldEdgeType = drawState->getVertexEdgeType(); drawState->setVertexEdgeType(GrDrawState::kQuad_EdgeType); target->drawIndexed(kTriangles_GrPrimitiveType, 0, // start vertex 0, // start index vCount, iCount); drawState->setVertexEdgeType(oldEdgeType); return true; }
void GrInOrderDrawBuffer::drawRect(const GrRect& rect, const GrMatrix* matrix, StageMask stageMask, const GrRect* srcRects[], const GrMatrix* srcMatrices[]) { GrAssert(!(NULL == fQuadIndexBuffer && fCurrQuad)); GrAssert(!(fDraws.empty() && fCurrQuad)); GrAssert(!(0 != fMaxQuads && NULL == fQuadIndexBuffer)); GrDrawState* drawState = this->drawState(); // if we have a quad IB then either append to the previous run of // rects or start a new run if (fMaxQuads) { bool appendToPreviousDraw = false; GrVertexLayout layout = GetRectVertexLayout(stageMask, srcRects); AutoReleaseGeometry geo(this, layout, 4, 0); if (!geo.succeeded()) { GrPrintf("Failed to get space for vertices!\n"); return; } GrMatrix combinedMatrix = drawState->getViewMatrix(); // We go to device space so that matrix changes allow us to concat // rect draws. When the caller has provided explicit source rects // then we don't want to modify the sampler matrices. Otherwise we do // we have to account for the view matrix change in the sampler // matrices. StageMask devCoordMask = (NULL == srcRects) ? stageMask : 0; GrDrawTarget::AutoDeviceCoordDraw adcd(this, devCoordMask); if (NULL != matrix) { combinedMatrix.preConcat(*matrix); } SetRectVertices(rect, &combinedMatrix, srcRects, srcMatrices, layout, geo.vertices()); // we don't want to miss an opportunity to batch rects together // simply because the clip has changed if the clip doesn't affect // the rect. bool disabledClip = false; if (drawState->isClipState() && fClip.isRect()) { GrRect clipRect = fClip.getRect(0); // If the clip rect touches the edge of the viewport, extended it // out (close) to infinity to avoid bogus intersections. // We might consider a more exact clip to viewport if this // conservative test fails. const GrRenderTarget* target = drawState->getRenderTarget(); if (0 >= clipRect.fLeft) { clipRect.fLeft = GR_ScalarMin; } if (target->width() <= clipRect.fRight) { clipRect.fRight = GR_ScalarMax; } if (0 >= clipRect.top()) { clipRect.fTop = GR_ScalarMin; } if (target->height() <= clipRect.fBottom) { clipRect.fBottom = GR_ScalarMax; } int stride = VertexSize(layout); bool insideClip = true; for (int v = 0; v < 4; ++v) { const GrPoint& p = *GetVertexPoint(geo.vertices(), v, stride); if (!clipRect.contains(p)) { insideClip = false; break; } } if (insideClip) { drawState->disableState(GrDrawState::kClip_StateBit); disabledClip = true; } } if (!needsNewClip() && !needsNewState() && fCurrQuad > 0 && fCurrQuad < fMaxQuads && layout == fLastRectVertexLayout) { int vsize = VertexSize(layout); Draw& lastDraw = fDraws.back(); GrAssert(lastDraw.fIndexBuffer == fQuadIndexBuffer); GrAssert(kTriangles_PrimitiveType == lastDraw.fPrimitiveType); GrAssert(0 == lastDraw.fVertexCount % 4); GrAssert(0 == lastDraw.fIndexCount % 6); GrAssert(0 == lastDraw.fStartIndex); GeometryPoolState& poolState = fGeoPoolStateStack.back(); bool clearSinceLastDraw = fClears.count() && fClears.back().fBeforeDrawIdx == fDraws.count(); appendToPreviousDraw = !clearSinceLastDraw && lastDraw.fVertexBuffer == poolState.fPoolVertexBuffer && (fCurrQuad * 4 + lastDraw.fStartVertex) == poolState.fPoolStartVertex; if (appendToPreviousDraw) { lastDraw.fVertexCount += 4; lastDraw.fIndexCount += 6; fCurrQuad += 1; // we reserved above, so we should be the first // use of this vertex reserveation. GrAssert(0 == poolState.fUsedPoolVertexBytes); poolState.fUsedPoolVertexBytes = 4 * vsize; } } if (!appendToPreviousDraw) { this->setIndexSourceToBuffer(fQuadIndexBuffer); this->drawIndexed(kTriangles_PrimitiveType, 0, 0, 4, 6); fCurrQuad = 1; fLastRectVertexLayout = layout; } if (disabledClip) { drawState->enableState(GrDrawState::kClip_StateBit); } fInstancedDrawTracker.reset(); } else { INHERITED::drawRect(rect, matrix, stageMask, srcRects, srcMatrices); } }
void GrInOrderDrawBuffer::drawRect(const GrRect& rect, const SkMatrix* matrix, const GrRect* srcRects[], const SkMatrix* srcMatrices[]) { GrVertexLayout layout = 0; GrDrawState::AutoColorRestore acr; GrColor color = this->drawState()->getColor(); // Using per-vertex colors allows batching across colors. (A lot of rects in a row differing // only in color is a common occurrence in tables). However, having per-vertex colors disables // blending optimizations because we don't know if the color will be solid or not. These // optimizations help determine whether coverage and color can be blended correctly when // dual-source blending isn't available. This comes into play when there is coverage. If colors // were a stage it could take a hint that every vertex's color will be opaque. if (this->getCaps().dualSourceBlendingSupport() || this->getDrawState().hasSolidCoverage(this->getGeomSrc().fVertexLayout)) { layout |= GrDrawState::kColor_VertexLayoutBit;; // We set the draw state's color to white here. This is done so that any batching performed // in our subclass's onDraw() won't get a false from GrDrawState::op== due to a color // mismatch. TODO: Once vertex layout is owned by GrDrawState it should skip comparing the // constant color in its op== when the kColor layout bit is set and then we can remove this. acr.set(this->drawState(), 0xFFFFFFFF); } uint32_t explicitCoordMask = 0; if (NULL != srcRects) { for (int s = 0; s < GrDrawState::kNumStages; ++s) { int numTC = 0; if (NULL != srcRects[s]) { layout |= GrDrawState::StageTexCoordVertexLayoutBit(s, numTC); ++numTC; explicitCoordMask |= (1 << s); } } } AutoReleaseGeometry geo(this, layout, 4, 0); if (!geo.succeeded()) { GrPrintf("Failed to get space for vertices!\n"); return; } // Go to device coords to allow batching across matrix changes SkMatrix combinedMatrix; if (NULL != matrix) { combinedMatrix = *matrix; } else { combinedMatrix.reset(); } combinedMatrix.postConcat(this->drawState()->getViewMatrix()); // When the caller has provided an explicit source rects for a stage then we don't want to // modify that stage's matrix. Otherwise if the effect is generating its source rect from // the vertex positions then we have to account for the view matrix change. GrDrawState::AutoDeviceCoordDraw adcd(this->drawState(), explicitCoordMask); if (!adcd.succeeded()) { return; } int stageOffsets[GrDrawState::kNumStages], colorOffset; int vsize = GrDrawState::VertexSizeAndOffsetsByStage(layout, stageOffsets, &colorOffset, NULL, NULL); geo.positions()->setRectFan(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, vsize); combinedMatrix.mapPointsWithStride(geo.positions(), vsize, 4); SkRect devBounds; // since we already computed the dev verts, set the bounds hint. This will help us avoid // unnecessary clipping in our onDraw(). get_vertex_bounds(geo.vertices(), vsize, 4, &devBounds); for (int i = 0; i < GrDrawState::kNumStages; ++i) { if (explicitCoordMask & (1 << i)) { GrAssert(0 != stageOffsets[i]); GrPoint* coords = GrTCast<GrPoint*>(GrTCast<intptr_t>(geo.vertices()) + stageOffsets[i]); coords->setRectFan(srcRects[i]->fLeft, srcRects[i]->fTop, srcRects[i]->fRight, srcRects[i]->fBottom, vsize); if (NULL != srcMatrices && NULL != srcMatrices[i]) { srcMatrices[i]->mapPointsWithStride(coords, vsize, 4); } } else { GrAssert(0 == stageOffsets[i]); } } if (colorOffset >= 0) { GrColor* vertColor = GrTCast<GrColor*>(GrTCast<intptr_t>(geo.vertices()) + colorOffset); for (int i = 0; i < 4; ++i) { *vertColor = color; vertColor = (GrColor*) ((intptr_t) vertColor + vsize); } } this->setIndexSourceToBuffer(fGpu->getQuadIndexBuffer()); this->drawIndexedInstances(kTriangles_GrPrimitiveType, 1, 4, 6, &devBounds); }
void GrInOrderDrawBuffer::onDrawRect(const GrRect& rect, const SkMatrix* matrix, const GrRect* localRect, const SkMatrix* localMatrix) { GrDrawState::AutoColorRestore acr; GrDrawState* drawState = this->drawState(); GrColor color = drawState->getColor(); int colorOffset, localOffset; set_vertex_attributes(drawState, this->caps()->dualSourceBlendingSupport() || drawState->hasSolidCoverage(), NULL != localRect, &colorOffset, &localOffset); if (colorOffset >= 0) { // We set the draw state's color to white here. This is done so that any batching performed // in our subclass's onDraw() won't get a false from GrDrawState::op== due to a color // mismatch. TODO: Once vertex layout is owned by GrDrawState it should skip comparing the // constant color in its op== when the kColor layout bit is set and then we can remove // this. acr.set(drawState, 0xFFFFFFFF); } AutoReleaseGeometry geo(this, 4, 0); if (!geo.succeeded()) { GrPrintf("Failed to get space for vertices!\n"); return; } // Go to device coords to allow batching across matrix changes SkMatrix combinedMatrix; if (NULL != matrix) { combinedMatrix = *matrix; } else { combinedMatrix.reset(); } combinedMatrix.postConcat(drawState->getViewMatrix()); // When the caller has provided an explicit source rect for a stage then we don't want to // modify that stage's matrix. Otherwise if the effect is generating its source rect from // the vertex positions then we have to account for the view matrix change. GrDrawState::AutoDeviceCoordDraw adcd(drawState); if (!adcd.succeeded()) { return; } size_t vsize = drawState->getVertexSize(); geo.positions()->setRectFan(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, vsize); combinedMatrix.mapPointsWithStride(geo.positions(), vsize, 4); SkRect devBounds; // since we already computed the dev verts, set the bounds hint. This will help us avoid // unnecessary clipping in our onDraw(). get_vertex_bounds(geo.vertices(), vsize, 4, &devBounds); if (localOffset >= 0) { GrPoint* coords = GrTCast<GrPoint*>(GrTCast<intptr_t>(geo.vertices()) + localOffset); coords->setRectFan(localRect->fLeft, localRect->fTop, localRect->fRight, localRect->fBottom, vsize); if (NULL != localMatrix) { localMatrix->mapPointsWithStride(coords, vsize, 4); } } if (colorOffset >= 0) { GrColor* vertColor = GrTCast<GrColor*>(GrTCast<intptr_t>(geo.vertices()) + colorOffset); for (int i = 0; i < 4; ++i) { *vertColor = color; vertColor = (GrColor*) ((intptr_t) vertColor + vsize); } } this->setIndexSourceToBuffer(this->getContext()->getQuadIndexBuffer()); this->drawIndexedInstances(kTriangles_GrPrimitiveType, 1, 4, 6, &devBounds); // to ensure that stashing the drawState ptr is valid GrAssert(this->drawState() == drawState); }
void GrInOrderDrawBuffer::drawRect(const GrRect& rect, const SkMatrix* matrix, const GrRect* srcRects[], const SkMatrix* srcMatrices[]) { GrAssert(!(NULL == fQuadIndexBuffer && fCurrQuad)); GrAssert(!(fDraws.empty() && fCurrQuad)); GrAssert(!(0 != fMaxQuads && NULL == fQuadIndexBuffer)); GrDrawState* drawState = this->drawState(); // if we have a quad IB then either append to the previous run of // rects or start a new run if (fMaxQuads) { bool appendToPreviousDraw = false; GrVertexLayout layout = GetRectVertexLayout(srcRects); // Batching across colors means we move the draw color into the // rect's vertex colors to allow greater batching (a lot of rects // in a row differing only in color is a common occurence in tables). bool batchAcrossColors = true; if (!this->getCaps().dualSourceBlendingSupport()) { for (int s = 0; s < GrDrawState::kNumStages; ++s) { if (this->getDrawState().isStageEnabled(s)) { // We disable batching across colors when there is a texture // present because (by pushing the the color to the vertices) // Ganesh loses track of the rect's opacity. This, in turn, can // cause some of the blending optimizations to be disabled. This // becomes a huge problem on some of the smaller devices where // shader derivatives and dual source blending aren't supported. // In those cases paths are often drawn to a texture and then // drawn as a texture (using this method). Because dual source // blending is disabled (and the blend optimizations are short // circuited) some of the more esoteric blend modes can no longer // be supported. // TODO: add tracking of batchAcrossColors's opacity batchAcrossColors = false; break; } } } if (batchAcrossColors) { layout |= GrDrawState::kColor_VertexLayoutBit; } AutoReleaseGeometry geo(this, layout, 4, 0); if (!geo.succeeded()) { GrPrintf("Failed to get space for vertices!\n"); return; } SkMatrix combinedMatrix = drawState->getViewMatrix(); // We go to device space so that matrix changes allow us to concat // rect draws. When the caller has provided explicit source rects // then we don't want to modify the stages' matrices. Otherwise // we have to account for the view matrix change in the stage // matrices. uint32_t explicitCoordMask = 0; if (srcRects) { for (int s = 0; s < GrDrawState::kNumStages; ++s) { if (srcRects[s]) { explicitCoordMask |= (1 << s); } } } GrDrawState::AutoDeviceCoordDraw adcd(this->drawState(), explicitCoordMask); if (!adcd.succeeded()) { return; } if (NULL != matrix) { combinedMatrix.preConcat(*matrix); } SetRectVertices(rect, &combinedMatrix, srcRects, srcMatrices, this->getDrawState().getColor(), layout, geo.vertices()); // Now that the paint's color is stored in the vertices set it to // white so that the following code can batch all the rects regardless // of paint color GrDrawState::AutoColorRestore acr(this->drawState(), batchAcrossColors ? SK_ColorWHITE : this->getDrawState().getColor()); // we don't want to miss an opportunity to batch rects together // simply because the clip has changed if the clip doesn't affect // the rect. bool disabledClip = false; if (drawState->isClipState()) { GrRect devClipRect; bool isIntersectionOfRects = false; const GrClipData* clip = this->getClip(); clip->fClipStack->getConservativeBounds(-clip->fOrigin.fX, -clip->fOrigin.fY, drawState->getRenderTarget()->width(), drawState->getRenderTarget()->height(), &devClipRect, &isIntersectionOfRects); if (isIntersectionOfRects) { // If the clip rect touches the edge of the viewport, extended it // out (close) to infinity to avoid bogus intersections. // We might consider a more exact clip to viewport if this // conservative test fails. const GrRenderTarget* target = drawState->getRenderTarget(); if (0 >= devClipRect.fLeft) { devClipRect.fLeft = SK_ScalarMin; } if (target->width() <= devClipRect.fRight) { devClipRect.fRight = SK_ScalarMax; } if (0 >= devClipRect.top()) { devClipRect.fTop = SK_ScalarMin; } if (target->height() <= devClipRect.fBottom) { devClipRect.fBottom = SK_ScalarMax; } int stride = GrDrawState::VertexSize(layout); bool insideClip = true; for (int v = 0; v < 4; ++v) { const GrPoint& p = *GrDrawState::GetVertexPoint(geo.vertices(), v, stride); if (!devClipRect.contains(p)) { insideClip = false; break; } } if (insideClip) { drawState->disableState(GrDrawState::kClip_StateBit); disabledClip = true; } } } if (!this->needsNewClip() && !this->needsNewState() && fCurrQuad > 0 && fCurrQuad < fMaxQuads && layout == fLastRectVertexLayout) { int vsize = GrDrawState::VertexSize(layout); Draw& lastDraw = fDraws.back(); GrAssert(lastDraw.fIndexBuffer == fQuadIndexBuffer); GrAssert(kTriangles_GrPrimitiveType == lastDraw.fPrimitiveType); GrAssert(0 == lastDraw.fVertexCount % 4); GrAssert(0 == lastDraw.fIndexCount % 6); GrAssert(0 == lastDraw.fStartIndex); GeometryPoolState& poolState = fGeoPoolStateStack.back(); appendToPreviousDraw = kDraw_Cmd == fCmds.back() && lastDraw.fVertexBuffer == poolState.fPoolVertexBuffer && (fCurrQuad * 4 + lastDraw.fStartVertex) == poolState.fPoolStartVertex; if (appendToPreviousDraw) { lastDraw.fVertexCount += 4; lastDraw.fIndexCount += 6; fCurrQuad += 1; // we reserved above, so we should be the first // use of this vertex reservation. GrAssert(0 == poolState.fUsedPoolVertexBytes); poolState.fUsedPoolVertexBytes = 4 * vsize; } } if (!appendToPreviousDraw) { this->setIndexSourceToBuffer(fQuadIndexBuffer); this->drawIndexed(kTriangles_GrPrimitiveType, 0, 0, 4, 6); fCurrQuad = 1; fLastRectVertexLayout = layout; } if (disabledClip) { drawState->enableState(GrDrawState::kClip_StateBit); } fInstancedDrawTracker.reset(); } else { INHERITED::drawRect(rect, matrix, srcRects, srcMatrices); } }