Index: src/gpu/effects/GrDashingEffect.cpp |
diff --git a/src/gpu/effects/GrDashingEffect.cpp b/src/gpu/effects/GrDashingEffect.cpp |
index afeff437f127c6ef1efe129604bdb906f7387dcf..f9d526f7e7b34438a85c0896d7bc97b18dd8f768 100644 |
--- a/src/gpu/effects/GrDashingEffect.cpp |
+++ b/src/gpu/effects/GrDashingEffect.cpp |
@@ -7,8 +7,9 @@ |
#include "GrDashingEffect.h" |
-#include "../GrAARectRenderer.h" |
- |
+#include "GrBatch.h" |
+#include "GrBatchTarget.h" |
+#include "GrBufferAllocPool.h" |
#include "GrGeometryProcessor.h" |
#include "GrContext.h" |
#include "GrCoordTransform.h" |
@@ -114,21 +115,21 @@ static void align_to_x_axis(const SkPoint pts[2], SkMatrix* rotMatrix, SkPoint p |
} |
// Assumes phase < sum of all intervals |
-static SkScalar calc_start_adjustment(const SkPathEffect::DashInfo& info) { |
- SkASSERT(info.fPhase < info.fIntervals[0] + info.fIntervals[1]); |
- if (info.fPhase >= info.fIntervals[0] && info.fPhase != 0) { |
- SkScalar srcIntervalLen = info.fIntervals[0] + info.fIntervals[1]; |
- return srcIntervalLen - info.fPhase; |
+static SkScalar calc_start_adjustment(const SkScalar intervals[2], SkScalar phase) { |
+ SkASSERT(phase < intervals[0] + intervals[1]); |
+ if (phase >= intervals[0] && phase != 0) { |
+ SkScalar srcIntervalLen = intervals[0] + intervals[1]; |
+ return srcIntervalLen - phase; |
} |
return 0; |
} |
-static SkScalar calc_end_adjustment(const SkPathEffect::DashInfo& info, const SkPoint pts[2], |
+static SkScalar calc_end_adjustment(const SkScalar intervals[2], const SkPoint pts[2], |
SkScalar phase, SkScalar* endingInt) { |
if (pts[1].fX <= pts[0].fX) { |
return 0; |
} |
- SkScalar srcIntervalLen = info.fIntervals[0] + info.fIntervals[1]; |
+ SkScalar srcIntervalLen = intervals[0] + intervals[1]; |
SkScalar totalLen = pts[1].fX - pts[0].fX; |
SkScalar temp = SkScalarDiv(totalLen, srcIntervalLen); |
SkScalar numFullIntervals = SkScalarFloorToScalar(temp); |
@@ -138,11 +139,11 @@ static SkScalar calc_end_adjustment(const SkPathEffect::DashInfo& info, const Sk |
if (0 == *endingInt) { |
*endingInt = srcIntervalLen; |
} |
- if (*endingInt > info.fIntervals[0]) { |
- if (0 == info.fIntervals[0]) { |
+ if (*endingInt > intervals[0]) { |
+ if (0 == intervals[0]) { |
*endingInt -= 0.01f; // make sure we capture the last zero size pnt (used if has caps) |
} |
- return *endingInt - info.fIntervals[0]; |
+ return *endingInt - intervals[0]; |
} |
return 0; |
} |
@@ -237,285 +238,502 @@ static GrGeometryProcessor* create_dash_gp(GrColor, |
DashCap cap, |
const SkMatrix& localMatrix); |
-bool GrDashingEffect::DrawDashLine(GrGpu* gpu, GrDrawTarget* target, |
- GrPipelineBuilder* pipelineBuilder, GrColor color, |
- const SkMatrix& viewMatrix, const SkPoint pts[2], |
- const GrPaint& paint, const GrStrokeInfo& strokeInfo) { |
- if (!can_fast_path_dash(pts, strokeInfo, *target, *pipelineBuilder, viewMatrix)) { |
- return false; |
+class DashBatch : public GrBatch { |
+public: |
+ struct Geometry { |
+ GrColor fColor; |
+ SkMatrix fViewMatrix; |
+ SkMatrix fSrcRotInv; |
+ SkPoint fPtsRot[2]; |
+ SkScalar fSrcStrokeWidth; |
+ SkScalar fPhase; |
+ SkScalar fIntervals[2]; |
+ SkScalar fParallelScale; |
+ SkScalar fPerpendicularScale; |
+ SkDEBUGCODE(SkRect fDevBounds;) |
+ }; |
+ |
+ static GrBatch* Create(const Geometry& geometry, SkPaint::Cap cap, bool useAA, bool fullDash) { |
+ return SkNEW_ARGS(DashBatch, (geometry, cap, useAA, fullDash)); |
} |
- const SkPathEffect::DashInfo& info = strokeInfo.getDashInfo(); |
+ const char* name() const SK_OVERRIDE { return "DashBatch"; } |
- SkPaint::Cap cap = strokeInfo.getStrokeRec().getCap(); |
+ void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE { |
+ // When this is called on a batch, there is only one geometry bundle |
+ out->setKnownFourComponents(fGeoData[0].fColor); |
+ } |
+ void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE { |
+ out->setUnknownSingleComponent(); |
+ } |
- SkScalar srcStrokeWidth = strokeInfo.getStrokeRec().getWidth(); |
+ void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE { |
+ // Handle any color overrides |
+ if (init.fColorIgnored) { |
+ fGeoData[0].fColor = GrColor_ILLEGAL; |
+ } else if (GrColor_ILLEGAL != init.fOverrideColor) { |
+ fGeoData[0].fColor = init.fOverrideColor; |
+ } |
- // the phase should be normalized to be [0, sum of all intervals) |
- SkASSERT(info.fPhase >= 0 && info.fPhase < info.fIntervals[0] + info.fIntervals[1]); |
+ // setup batch properties |
+ fBatch.fColorIgnored = init.fColorIgnored; |
+ fBatch.fColor = fGeoData[0].fColor; |
+ fBatch.fUsesLocalCoords = init.fUsesLocalCoords; |
+ fBatch.fCoverageIgnored = init.fCoverageIgnored; |
+ } |
- SkScalar srcPhase = info.fPhase; |
+ struct DashDraw { |
+ SkScalar fStartOffset; |
+ SkScalar fStrokeWidth; |
+ SkScalar fLineLength; |
+ SkScalar fHalfDevStroke; |
+ SkScalar fDevBloat; |
+ bool fLineDone; |
+ bool fHasStartRect; |
+ bool fHasEndRect; |
+ }; |
+ |
+ void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE { |
+ int instanceCount = fGeoData.count(); |
+ |
+ SkMatrix invert; |
+ if (this->usesLocalCoords() && !this->viewMatrix().invert(&invert)) { |
+ SkDebugf("Failed to invert\n"); |
+ return; |
+ } |
- // Rotate the src pts so they are aligned horizontally with pts[0].fX < pts[1].fX |
- SkMatrix srcRotInv; |
- SkPoint ptsRot[2]; |
- if (pts[0].fY != pts[1].fY || pts[0].fX > pts[1].fX) { |
- SkMatrix rotMatrix; |
- align_to_x_axis(pts, &rotMatrix, ptsRot); |
- if(!rotMatrix.invert(&srcRotInv)) { |
- SkDebugf("Failed to create invertible rotation matrix!\n"); |
- return false; |
+ SkPaint::Cap cap = this->cap(); |
+ |
+ SkAutoTUnref<const GrGeometryProcessor> gp; |
+ |
+ bool isRoundCap = SkPaint::kRound_Cap == cap; |
+ DashCap capType = isRoundCap ? kRound_DashCap : kNonRound_DashCap; |
+ if (this->fullDash()) { |
+ GrPrimitiveEdgeType edgeType = this->useAA() ? kFillAA_GrProcessorEdgeType : |
+ kFillBW_GrProcessorEdgeType; |
+ gp.reset(create_dash_gp(this->color(), edgeType, capType, invert)); |
+ } else { |
+ // Set up the vertex data for the line and start/end dashes |
+ gp.reset(GrDefaultGeoProcFactory::Create(GrDefaultGeoProcFactory::kPosition_GPType, |
+ this->color(), |
+ SkMatrix::I(), |
+ invert)); |
} |
- } else { |
- srcRotInv.reset(); |
- memcpy(ptsRot, pts, 2 * sizeof(SkPoint)); |
- } |
- bool useAA = paint.isAntiAlias(); |
+ batchTarget->initDraw(gp, pipeline); |
+ |
+ // TODO remove this when batch is everywhere |
+ GrPipelineInfo init; |
+ init.fColorIgnored = fBatch.fColorIgnored; |
+ init.fOverrideColor = GrColor_ILLEGAL; |
+ init.fCoverageIgnored = fBatch.fCoverageIgnored; |
+ init.fUsesLocalCoords = this->usesLocalCoords(); |
+ gp->initBatchTracker(batchTarget->currentBatchTracker(), init); |
+ |
+ bool useAA = this->useAA(); |
+ bool fullDash = this->fullDash(); |
+ |
+ // We do two passes over all of the dashes. First we setup the start, end, and bounds, |
+ // rectangles. We preserve all of this work in the rects / draws arrays below. Then we |
+ // iterate again over these decomposed dashes to generate vertices |
+ SkSTArray<128, SkRect, true> rects; |
+ SkSTArray<128, DashDraw, true> draws; |
+ |
+ int totalRectCount = 0; |
+ for (int i = 0; i < instanceCount; i++) { |
+ Geometry& args = fGeoData[i]; |
+ |
+ bool hasCap = SkPaint::kButt_Cap != cap && 0 != args.fSrcStrokeWidth; |
+ |
+ // We always want to at least stroke out half a pixel on each side in device space |
+ // so 0.5f / perpScale gives us this min in src space |
+ SkScalar halfSrcStroke = |
+ SkMaxScalar(args.fSrcStrokeWidth * 0.5f, 0.5f / args.fPerpendicularScale); |
+ |
+ SkScalar strokeAdj; |
+ if (!hasCap) { |
+ strokeAdj = 0.f; |
+ } else { |
+ strokeAdj = halfSrcStroke; |
+ } |
- // Scale corrections of intervals and stroke from view matrix |
- SkScalar parallelScale; |
- SkScalar perpScale; |
- calc_dash_scaling(¶llelScale, &perpScale, viewMatrix, ptsRot); |
+ SkScalar startAdj = 0; |
+ |
+ SkMatrix& combinedMatrix = args.fSrcRotInv; |
+ combinedMatrix.postConcat(args.fViewMatrix); |
+ |
+ bool lineDone = false; |
+ |
+ // Too simplify the algorithm, we always push back rects for start and end rect. |
+ // Otherwise we'd have to track start / end rects for each individual geometry |
+ SkRect& bounds = rects.push_back(); |
+ SkRect& startRect = rects.push_back(); |
+ SkRect& endRect = rects.push_back(); |
+ |
+ bool hasStartRect = false; |
+ // If we are using AA, check to see if we are drawing a partial dash at the start. If so |
+ // draw it separately here and adjust our start point accordingly |
+ if (useAA) { |
+ if (args.fPhase > 0 && args.fPhase < args.fIntervals[0]) { |
+ SkPoint startPts[2]; |
+ startPts[0] = args.fPtsRot[0]; |
+ startPts[1].fY = startPts[0].fY; |
+ startPts[1].fX = SkMinScalar(startPts[0].fX + args.fIntervals[0] - args.fPhase, |
+ args.fPtsRot[1].fX); |
+ startRect.set(startPts, 2); |
+ startRect.outset(strokeAdj, halfSrcStroke); |
+ |
+ hasStartRect = true; |
+ startAdj = args.fIntervals[0] + args.fIntervals[1] - args.fPhase; |
+ } |
+ } |
- bool hasCap = SkPaint::kButt_Cap != cap && 0 != srcStrokeWidth; |
+ // adjustments for start and end of bounding rect so we only draw dash intervals |
+ // contained in the original line segment. |
+ startAdj += calc_start_adjustment(args.fIntervals, args.fPhase); |
+ if (startAdj != 0) { |
+ args.fPtsRot[0].fX += startAdj; |
+ args.fPhase = 0; |
+ } |
+ SkScalar endingInterval = 0; |
+ SkScalar endAdj = calc_end_adjustment(args.fIntervals, args.fPtsRot, args.fPhase, |
+ &endingInterval); |
+ args.fPtsRot[1].fX -= endAdj; |
+ if (args.fPtsRot[0].fX >= args.fPtsRot[1].fX) { |
+ lineDone = true; |
+ } |
- // We always want to at least stroke out half a pixel on each side in device space |
- // so 0.5f / perpScale gives us this min in src space |
- SkScalar halfSrcStroke = SkMaxScalar(srcStrokeWidth * 0.5f, 0.5f / perpScale); |
+ bool hasEndRect = false; |
+ // If we are using AA, check to see if we are drawing a partial dash at then end. If so |
+ // draw it separately here and adjust our end point accordingly |
+ if (useAA && !lineDone) { |
+ // If we adjusted the end then we will not be drawing a partial dash at the end. |
+ // If we didn't adjust the end point then we just need to make sure the ending |
+ // dash isn't a full dash |
+ if (0 == endAdj && endingInterval != args.fIntervals[0]) { |
+ SkPoint endPts[2]; |
+ endPts[1] = args.fPtsRot[1]; |
+ endPts[0].fY = endPts[1].fY; |
+ endPts[0].fX = endPts[1].fX - endingInterval; |
+ |
+ endRect.set(endPts, 2); |
+ endRect.outset(strokeAdj, halfSrcStroke); |
+ |
+ hasEndRect = true; |
+ endAdj = endingInterval + args.fIntervals[1]; |
+ |
+ args.fPtsRot[1].fX -= endAdj; |
+ if (args.fPtsRot[0].fX >= args.fPtsRot[1].fX) { |
+ lineDone = true; |
+ } |
+ } |
+ } |
- SkScalar strokeAdj; |
- if (!hasCap) { |
- strokeAdj = 0.f; |
- } else { |
- strokeAdj = halfSrcStroke; |
- } |
+ if (startAdj != 0) { |
+ args.fPhase = 0; |
+ } |
- SkScalar startAdj = 0; |
- |
- SkMatrix combinedMatrix = srcRotInv; |
- combinedMatrix.postConcat(viewMatrix); |
- |
- bool lineDone = false; |
- SkRect startRect; |
- bool hasStartRect = false; |
- // If we are using AA, check to see if we are drawing a partial dash at the start. If so |
- // draw it separately here and adjust our start point accordingly |
- if (useAA) { |
- if (srcPhase > 0 && srcPhase < info.fIntervals[0]) { |
- SkPoint startPts[2]; |
- startPts[0] = ptsRot[0]; |
- startPts[1].fY = startPts[0].fY; |
- startPts[1].fX = SkMinScalar(startPts[0].fX + info.fIntervals[0] - srcPhase, |
- ptsRot[1].fX); |
- startRect.set(startPts, 2); |
- startRect.outset(strokeAdj, halfSrcStroke); |
- |
- hasStartRect = true; |
- startAdj = info.fIntervals[0] + info.fIntervals[1] - srcPhase; |
- } |
- } |
+ // Change the dashing info from src space into device space |
+ SkScalar* devIntervals = args.fIntervals; |
+ devIntervals[0] = args.fIntervals[0] * args.fParallelScale; |
+ devIntervals[1] = args.fIntervals[1] * args.fParallelScale; |
+ SkScalar devPhase = args.fPhase * args.fParallelScale; |
+ SkScalar strokeWidth = args.fSrcStrokeWidth * args.fPerpendicularScale; |
- // adjustments for start and end of bounding rect so we only draw dash intervals |
- // contained in the original line segment. |
- startAdj += calc_start_adjustment(info); |
- if (startAdj != 0) { |
- ptsRot[0].fX += startAdj; |
- srcPhase = 0; |
- } |
- SkScalar endingInterval = 0; |
- SkScalar endAdj = calc_end_adjustment(info, ptsRot, srcPhase, &endingInterval); |
- ptsRot[1].fX -= endAdj; |
- if (ptsRot[0].fX >= ptsRot[1].fX) { |
- lineDone = true; |
- } |
+ if ((strokeWidth < 1.f && !useAA) || 0.f == strokeWidth) { |
+ strokeWidth = 1.f; |
+ } |
+ |
+ SkScalar halfDevStroke = strokeWidth * 0.5f; |
- SkRect endRect; |
- bool hasEndRect = false; |
- // If we are using AA, check to see if we are drawing a partial dash at then end. If so |
- // draw it separately here and adjust our end point accordingly |
- if (useAA && !lineDone) { |
- // If we adjusted the end then we will not be drawing a partial dash at the end. |
- // If we didn't adjust the end point then we just need to make sure the ending |
- // dash isn't a full dash |
- if (0 == endAdj && endingInterval != info.fIntervals[0]) { |
- SkPoint endPts[2]; |
- endPts[1] = ptsRot[1]; |
- endPts[0].fY = endPts[1].fY; |
- endPts[0].fX = endPts[1].fX - endingInterval; |
- |
- endRect.set(endPts, 2); |
- endRect.outset(strokeAdj, halfSrcStroke); |
- |
- hasEndRect = true; |
- endAdj = endingInterval + info.fIntervals[1]; |
- |
- ptsRot[1].fX -= endAdj; |
- if (ptsRot[0].fX >= ptsRot[1].fX) { |
+ if (SkPaint::kSquare_Cap == cap && 0 != args.fSrcStrokeWidth) { |
+ // add cap to on interval and remove from off interval |
+ devIntervals[0] += strokeWidth; |
+ devIntervals[1] -= strokeWidth; |
+ } |
+ SkScalar startOffset = devIntervals[1] * 0.5f + devPhase; |
+ |
+ SkScalar bloatX = useAA ? 0.5f / args.fParallelScale : 0.f; |
+ SkScalar bloatY = useAA ? 0.5f / args.fPerpendicularScale : 0.f; |
+ |
+ SkScalar devBloat = useAA ? 0.5f : 0.f; |
+ |
+ if (devIntervals[1] <= 0.f && useAA) { |
+ // Case when we end up drawing a solid AA rect |
+ // Reset the start rect to draw this single solid rect |
+ // but it requires to upload a new intervals uniform so we can mimic |
+ // one giant dash |
+ args.fPtsRot[0].fX -= hasStartRect ? startAdj : 0; |
+ args.fPtsRot[1].fX += hasEndRect ? endAdj : 0; |
+ startRect.set(args.fPtsRot, 2); |
+ startRect.outset(strokeAdj, halfSrcStroke); |
+ hasStartRect = true; |
+ hasEndRect = false; |
lineDone = true; |
+ |
+ SkPoint devicePts[2]; |
+ args.fViewMatrix.mapPoints(devicePts, args.fPtsRot, 2); |
+ SkScalar lineLength = SkPoint::Distance(devicePts[0], devicePts[1]); |
+ if (hasCap) { |
+ lineLength += 2.f * halfDevStroke; |
+ } |
+ devIntervals[0] = lineLength; |
+ } |
+ |
+ totalRectCount += !lineDone ? 1 : 0; |
+ totalRectCount += hasStartRect ? 1 : 0; |
+ totalRectCount += hasEndRect ? 1 : 0; |
+ |
+ if (SkPaint::kRound_Cap == cap && 0 != args.fSrcStrokeWidth) { |
+ // need to adjust this for round caps to correctly set the dashPos attrib on |
+ // vertices |
+ startOffset -= halfDevStroke; |
+ } |
+ |
+ DashDraw& draw = draws.push_back(); |
+ if (!lineDone) { |
+ SkPoint devicePts[2]; |
+ args.fViewMatrix.mapPoints(devicePts, args.fPtsRot, 2); |
+ draw.fLineLength = SkPoint::Distance(devicePts[0], devicePts[1]); |
+ if (hasCap) { |
+ draw.fLineLength += 2.f * halfDevStroke; |
+ } |
+ |
+ bounds.set(args.fPtsRot[0].fX, args.fPtsRot[0].fY, |
+ args.fPtsRot[1].fX, args.fPtsRot[1].fY); |
+ bounds.outset(bloatX + strokeAdj, bloatY + halfSrcStroke); |
+ } |
+ |
+ if (hasStartRect) { |
+ SkASSERT(useAA); // so that we know bloatX and bloatY have been set |
+ startRect.outset(bloatX, bloatY); |
+ } |
+ |
+ if (hasEndRect) { |
+ SkASSERT(useAA); // so that we know bloatX and bloatY have been set |
+ endRect.outset(bloatX, bloatY); |
+ } |
+ |
+ draw.fStartOffset = startOffset; |
+ draw.fDevBloat = devBloat; |
+ draw.fHalfDevStroke = halfDevStroke; |
+ draw.fStrokeWidth = strokeWidth; |
+ draw.fHasStartRect = hasStartRect; |
+ draw.fLineDone = lineDone; |
+ draw.fHasEndRect = hasEndRect; |
+ } |
+ |
+ const GrVertexBuffer* vertexBuffer; |
+ int firstVertex; |
+ |
+ size_t vertexStride = gp->getVertexStride(); |
+ void* vertices = batchTarget->vertexPool()->makeSpace(vertexStride, |
+ totalRectCount * kVertsPerDash, |
+ &vertexBuffer, |
+ &firstVertex); |
+ |
+ int curVIdx = 0; |
+ int rectIndex = 0; |
+ for (int i = 0; i < instanceCount; i++) { |
+ Geometry& args = fGeoData[i]; |
+ |
+ if (!draws[i].fLineDone) { |
+ if (fullDash) { |
+ setup_dashed_rect(rects[rectIndex], vertices, curVIdx, args.fSrcRotInv, |
+ draws[i].fStartOffset, draws[i].fDevBloat, |
+ draws[i].fLineLength, draws[i].fHalfDevStroke, |
+ args.fIntervals[0], args.fIntervals[1], draws[i].fStrokeWidth, |
+ capType, gp->getVertexStride()); |
+ } else { |
+ SkPoint* verts = reinterpret_cast<SkPoint*>(vertices); |
+ SkASSERT(gp->getVertexStride() == sizeof(SkPoint)); |
+ setup_dashed_rect_pos(rects[rectIndex], curVIdx, args.fSrcRotInv, verts); |
+ } |
+ curVIdx += 4; |
+ } |
+ rectIndex++; |
+ |
+ if (draws[i].fHasStartRect) { |
+ if (fullDash) { |
+ setup_dashed_rect(rects[rectIndex], vertices, curVIdx, args.fSrcRotInv, |
+ draws[i].fStartOffset, draws[i].fDevBloat, args.fIntervals[0], |
+ draws[i].fHalfDevStroke, args.fIntervals[0], |
+ args.fIntervals[1], draws[i].fStrokeWidth, capType, |
+ gp->getVertexStride()); |
+ } else { |
+ SkPoint* verts = reinterpret_cast<SkPoint*>(vertices); |
+ SkASSERT(gp->getVertexStride() == sizeof(SkPoint)); |
+ setup_dashed_rect_pos(rects[rectIndex], curVIdx, args.fSrcRotInv, verts); |
+ } |
+ |
+ curVIdx += 4; |
} |
+ rectIndex++; |
+ |
+ if (draws[i].fHasEndRect) { |
+ if (fullDash) { |
+ setup_dashed_rect(rects[rectIndex], vertices, curVIdx, args.fSrcRotInv, |
+ draws[i].fStartOffset, draws[i].fDevBloat, args.fIntervals[0], |
+ draws[i].fHalfDevStroke, args.fIntervals[0], |
+ args.fIntervals[1], draws[i].fStrokeWidth, capType, |
+ gp->getVertexStride()); |
+ } else { |
+ SkPoint* verts = reinterpret_cast<SkPoint*>(vertices); |
+ SkASSERT(gp->getVertexStride() == sizeof(SkPoint)); |
+ setup_dashed_rect_pos(rects[rectIndex], curVIdx, args.fSrcRotInv, verts); |
+ } |
+ curVIdx += 4; |
+ } |
+ rectIndex++; |
} |
- } |
- if (startAdj != 0) { |
- srcPhase = 0; |
+ const GrIndexBuffer* dashIndexBuffer = batchTarget->quadIndexBuffer(); |
+ |
+ GrDrawTarget::DrawInfo drawInfo; |
+ drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType); |
+ drawInfo.setStartVertex(0); |
+ drawInfo.setStartIndex(0); |
+ drawInfo.setVerticesPerInstance(kVertsPerDash); |
+ drawInfo.setIndicesPerInstance(kIndicesPerDash); |
+ drawInfo.adjustStartVertex(firstVertex); |
+ drawInfo.setVertexBuffer(vertexBuffer); |
+ drawInfo.setIndexBuffer(dashIndexBuffer); |
+ |
+ int maxInstancesPerDraw = dashIndexBuffer->maxQuads(); |
+ while (totalRectCount) { |
+ drawInfo.setInstanceCount(SkTMin(totalRectCount, maxInstancesPerDraw)); |
+ drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance()); |
+ drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance()); |
+ |
+ batchTarget->draw(drawInfo); |
+ |
+ drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount()); |
+ totalRectCount -= drawInfo.instanceCount(); |
+ } |
} |
- // Change the dashing info from src space into device space |
- SkScalar devIntervals[2]; |
- devIntervals[0] = info.fIntervals[0] * parallelScale; |
- devIntervals[1] = info.fIntervals[1] * parallelScale; |
- SkScalar devPhase = srcPhase * parallelScale; |
- SkScalar strokeWidth = srcStrokeWidth * perpScale; |
+ SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; } |
+ |
+private: |
+ DashBatch(const Geometry& geometry, SkPaint::Cap cap, bool useAA, bool fullDash) { |
+ this->initClassID<DashBatch>(); |
+ fGeoData.push_back(geometry); |
- if ((strokeWidth < 1.f && !useAA) || 0.f == strokeWidth) { |
- strokeWidth = 1.f; |
+ fBatch.fUseAA = useAA; |
+ fBatch.fCap = cap; |
+ fBatch.fFullDash = fullDash; |
} |
- SkScalar halfDevStroke = strokeWidth * 0.5f; |
+ bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE { |
+ DashBatch* that = t->cast<DashBatch>(); |
- if (SkPaint::kSquare_Cap == cap && 0 != srcStrokeWidth) { |
- // add cap to on interveal and remove from off interval |
- devIntervals[0] += strokeWidth; |
- devIntervals[1] -= strokeWidth; |
- } |
- SkScalar startOffset = devIntervals[1] * 0.5f + devPhase; |
- |
- SkScalar bloatX = useAA ? 0.5f / parallelScale : 0.f; |
- SkScalar bloatY = useAA ? 0.5f / perpScale : 0.f; |
- |
- SkScalar devBloat = useAA ? 0.5f : 0.f; |
- |
- if (devIntervals[1] <= 0.f && useAA) { |
- // Case when we end up drawing a solid AA rect |
- // Reset the start rect to draw this single solid rect |
- // but it requires to upload a new intervals uniform so we can mimic |
- // one giant dash |
- ptsRot[0].fX -= hasStartRect ? startAdj : 0; |
- ptsRot[1].fX += hasEndRect ? endAdj : 0; |
- startRect.set(ptsRot, 2); |
- startRect.outset(strokeAdj, halfSrcStroke); |
- hasStartRect = true; |
- hasEndRect = false; |
- lineDone = true; |
- |
- SkPoint devicePts[2]; |
- viewMatrix.mapPoints(devicePts, ptsRot, 2); |
- SkScalar lineLength = SkPoint::Distance(devicePts[0], devicePts[1]); |
- if (hasCap) { |
- lineLength += 2.f * halfDevStroke; |
+ if (this->useAA() != that->useAA()) { |
+ return false; |
} |
- devIntervals[0] = lineLength; |
- } |
- // reset to device coordinates |
- SkMatrix invert; |
- if (!viewMatrix.invert(&invert)) { |
- SkDebugf("Failed to invert\n"); |
- return false; |
- } |
+ if (this->fullDash() != that->fullDash()) { |
+ return false; |
+ } |
- bool isRoundCap = SkPaint::kRound_Cap == cap; |
- DashCap capType = isRoundCap ? kRound_DashCap : kNonRound_DashCap; |
- |
- SkAutoTUnref<const GrGeometryProcessor> gp; |
- bool fullDash = devIntervals[1] > 0.f || useAA; |
- if (fullDash) { |
- SkPathEffect::DashInfo devInfo; |
- devInfo.fPhase = devPhase; |
- devInfo.fCount = 2; |
- devInfo.fIntervals = devIntervals; |
- GrPrimitiveEdgeType edgeType = useAA ? kFillAA_GrProcessorEdgeType : |
- kFillBW_GrProcessorEdgeType; |
- gp.reset(create_dash_gp(color, edgeType, capType, invert)); |
- } else { |
- // Set up the vertex data for the line and start/end dashes |
- gp.reset(GrDefaultGeoProcFactory::Create(GrDefaultGeoProcFactory::kPosition_GPType, |
- color, |
- SkMatrix::I(), |
- invert)); |
+ if (this->cap() != that->cap()) { |
+ return false; |
+ } |
+ |
+ // TODO vertex color |
+ if (this->color() != that->color()) { |
+ return false; |
+ } |
+ |
+ SkASSERT(this->usesLocalCoords() == that->usesLocalCoords()); |
+ if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) { |
+ return false; |
+ } |
+ |
+ fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin()); |
+ return true; |
} |
- int totalRectCnt = 0; |
+ GrColor color() const { return fBatch.fColor; } |
+ bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; } |
+ const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; } |
+ bool useAA() const { return fBatch.fUseAA; } |
+ bool fullDash() const { return fBatch.fFullDash; } |
+ SkPaint::Cap cap() const { return fBatch.fCap; } |
+ |
+ struct BatchTracker { |
+ GrColor fColor; |
+ bool fUsesLocalCoords; |
+ bool fColorIgnored; |
+ bool fCoverageIgnored; |
+ SkPaint::Cap fCap; |
+ bool fUseAA; |
+ bool fFullDash; |
+ }; |
+ |
+ static const int kVertsPerDash = 4; |
+ static const int kIndicesPerDash = 6; |
+ |
+ BatchTracker fBatch; |
+ SkSTArray<1, Geometry, true> fGeoData; |
+}; |
- totalRectCnt += !lineDone ? 1 : 0; |
- totalRectCnt += hasStartRect ? 1 : 0; |
- totalRectCnt += hasEndRect ? 1 : 0; |
- GrDrawTarget::AutoReleaseGeometry geo(target, |
- totalRectCnt * 4, |
- gp->getVertexStride(), 0); |
- if (!geo.succeeded()) { |
- SkDebugf("Failed to get space for vertices!\n"); |
+bool GrDashingEffect::DrawDashLine(GrGpu* gpu, GrDrawTarget* target, |
+ GrPipelineBuilder* pipelineBuilder, GrColor color, |
+ const SkMatrix& viewMatrix, const SkPoint pts[2], |
+ const GrPaint& paint, const GrStrokeInfo& strokeInfo) { |
+ if (!can_fast_path_dash(pts, strokeInfo, *target, *pipelineBuilder, viewMatrix)) { |
return false; |
} |
- int curVIdx = 0; |
+ const SkPathEffect::DashInfo& info = strokeInfo.getDashInfo(); |
- if (SkPaint::kRound_Cap == cap && 0 != srcStrokeWidth) { |
- // need to adjust this for round caps to correctly set the dashPos attrib on vertices |
- startOffset -= halfDevStroke; |
- } |
+ SkPaint::Cap cap = strokeInfo.getStrokeRec().getCap(); |
- // Draw interior part of dashed line |
- if (!lineDone) { |
- SkPoint devicePts[2]; |
- viewMatrix.mapPoints(devicePts, ptsRot, 2); |
- SkScalar lineLength = SkPoint::Distance(devicePts[0], devicePts[1]); |
- if (hasCap) { |
- lineLength += 2.f * halfDevStroke; |
- } |
+ DashBatch::Geometry geometry; |
+ geometry.fSrcStrokeWidth = strokeInfo.getStrokeRec().getWidth(); |
- SkRect bounds; |
- bounds.set(ptsRot[0].fX, ptsRot[0].fY, ptsRot[1].fX, ptsRot[1].fY); |
- bounds.outset(bloatX + strokeAdj, bloatY + halfSrcStroke); |
- if (fullDash) { |
- setup_dashed_rect(bounds, geo.vertices(), curVIdx, combinedMatrix, startOffset, |
- devBloat, lineLength, halfDevStroke, devIntervals[0], devIntervals[1], |
- strokeWidth, capType, gp->getVertexStride()); |
- } else { |
- SkPoint* verts = reinterpret_cast<SkPoint*>(geo.vertices()); |
- SkASSERT(gp->getVertexStride() == sizeof(SkPoint)); |
- setup_dashed_rect_pos(bounds, curVIdx, combinedMatrix, verts); |
+ // the phase should be normalized to be [0, sum of all intervals) |
+ SkASSERT(info.fPhase >= 0 && info.fPhase < info.fIntervals[0] + info.fIntervals[1]); |
+ |
+ // Rotate the src pts so they are aligned horizontally with pts[0].fX < pts[1].fX |
+ if (pts[0].fY != pts[1].fY || pts[0].fX > pts[1].fX) { |
+ SkMatrix rotMatrix; |
+ align_to_x_axis(pts, &rotMatrix, geometry.fPtsRot); |
+ if(!rotMatrix.invert(&geometry.fSrcRotInv)) { |
+ SkDebugf("Failed to create invertible rotation matrix!\n"); |
+ return false; |
} |
- curVIdx += 4; |
+ } else { |
+ geometry.fSrcRotInv.reset(); |
+ memcpy(geometry.fPtsRot, pts, 2 * sizeof(SkPoint)); |
} |
- if (hasStartRect) { |
- SkASSERT(useAA); // so that we know bloatX and bloatY have been set |
- startRect.outset(bloatX, bloatY); |
- if (fullDash) { |
- setup_dashed_rect(startRect, geo.vertices(), curVIdx, combinedMatrix, startOffset, |
- devBloat, devIntervals[0], halfDevStroke, devIntervals[0], |
- devIntervals[1], strokeWidth, capType, gp->getVertexStride()); |
- } else { |
- SkPoint* verts = reinterpret_cast<SkPoint*>(geo.vertices()); |
- SkASSERT(gp->getVertexStride() == sizeof(SkPoint)); |
- setup_dashed_rect_pos(startRect, curVIdx, combinedMatrix, verts); |
- } |
+ // Scale corrections of intervals and stroke from view matrix |
+ calc_dash_scaling(&geometry.fParallelScale, &geometry.fPerpendicularScale, viewMatrix, |
+ geometry.fPtsRot); |
- curVIdx += 4; |
+ SkScalar offInterval = info.fIntervals[1] * geometry.fParallelScale; |
+ SkScalar strokeWidth = geometry.fSrcStrokeWidth * geometry.fPerpendicularScale; |
+ |
+ if (SkPaint::kSquare_Cap == cap && 0 != geometry.fSrcStrokeWidth) { |
+ // add cap to on interveal and remove from off interval |
+ offInterval -= strokeWidth; |
} |
- if (hasEndRect) { |
- SkASSERT(useAA); // so that we know bloatX and bloatY have been set |
- endRect.outset(bloatX, bloatY); |
- if (fullDash) { |
- setup_dashed_rect(endRect, geo.vertices(), curVIdx, combinedMatrix, startOffset, |
- devBloat, devIntervals[0], halfDevStroke, devIntervals[0], |
- devIntervals[1], strokeWidth, capType, gp->getVertexStride()); |
- } else { |
- SkPoint* verts = reinterpret_cast<SkPoint*>(geo.vertices()); |
- SkASSERT(gp->getVertexStride() == sizeof(SkPoint)); |
- setup_dashed_rect_pos(endRect, curVIdx, combinedMatrix, verts); |
- } |
+ bool useAA = paint.isAntiAlias(); |
+ // TODO we can do a real rect call if not using fulldash(ie no off interval, not using AA) |
+ bool fullDash = offInterval > 0.f || useAA; |
- } |
+ geometry.fColor = color; |
+ geometry.fViewMatrix = viewMatrix; |
+ geometry.fPhase = info.fPhase; |
+ geometry.fIntervals[0] = info.fIntervals[0]; |
+ geometry.fIntervals[1] = info.fIntervals[1]; |
+ |
+ SkAutoTUnref<GrBatch> batch(DashBatch::Create(geometry, cap, useAA, fullDash)); |
+ target->drawBatch(pipelineBuilder, batch); |
- target->setIndexSourceToBuffer(gpu->getContext()->getQuadIndexBuffer()); |
- target->drawIndexedInstances(pipelineBuilder, gp, kTriangles_GrPrimitiveType, totalRectCnt, |
- 4, 6); |
- target->resetIndexSource(); |
return true; |
} |