From e61c411c1258a323a010558c08de3d9f8d170dca Mon Sep 17 00:00:00 2001 From: egdaniel Date: Thu, 12 Jun 2014 10:24:21 -0700 Subject: [PATCH] Use vertex attributes for dash effect in gpu This will allow us to batch dashed lines together when drawing. Also, this removes the need for a coord transform matrix in the shader, thus we save the cost of uploading a new matrix uniform everytime we do a simple transform to the dashed line we are drawing. BUG=skia: R=bsalomon@google.com Author: egdaniel@google.com Review URL: https://codereview.chromium.org/326103002 --- expectations/gm/ignored-tests.txt | 8 + gm/dashing.cpp | 3 - include/core/SkStrokeRec.h | 4 + include/gpu/GrContext.h | 1 + src/core/SkStrokeRec.cpp | 10 +- src/gpu/GrContext.cpp | 24 ++- src/gpu/GrStrokeInfo.h | 15 +- src/gpu/SkGpuDevice.cpp | 11 +- src/gpu/effects/GrDashingEffect.cpp | 367 ++++++++++++++++++++++++------------ src/gpu/effects/GrDashingEffect.h | 10 +- 10 files changed, 321 insertions(+), 132 deletions(-) diff --git a/expectations/gm/ignored-tests.txt b/expectations/gm/ignored-tests.txt index 0997c0e..573593b 100644 --- a/expectations/gm/ignored-tests.txt +++ b/expectations/gm/ignored-tests.txt @@ -67,6 +67,14 @@ coloremoji # This CL actually fixes this GM's image distantclip +# Added by egdaniel for https://codereview.chromium.org/326103002/ +# This CL may cause minor changes to gpu dashing +dashing +dashing2 +dashing3 +dashing4 +dashcubics + # dandov: Fix for bitmap shader by taking into account if the bitmap is alpha only # https://codereview.chromium.org/318923005/ bitmapshaders diff --git a/gm/dashing.cpp b/gm/dashing.cpp index 5a812b0..55addc8 100644 --- a/gm/dashing.cpp +++ b/gm/dashing.cpp @@ -62,7 +62,6 @@ protected: canvas->translate(SkIntToScalar(20), SkIntToScalar(20)); canvas->translate(0, SK_ScalarHalf); - for (int width = 0; width <= 2; ++width) { for (size_t data = 0; data < SK_ARRAY_COUNT(gData); ++data) { for (int aa = 0; aa <= 1; ++aa) { @@ -234,7 +233,6 @@ protected: canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, p); } - } virtual void onDraw(SkCanvas* canvas) { @@ -372,7 +370,6 @@ protected: paint.setAntiAlias(SkToBool(aa)); paint.setStrokeWidth(8.f); paint.setStrokeCap(SkPaint::kSquare_Cap); - // Single dash element that is cut off at start and end drawline(canvas, 32, 16, paint, 20.f, 0, 5.f); canvas->translate(0, SkIntToScalar(20)); diff --git a/include/core/SkStrokeRec.h b/include/core/SkStrokeRec.h index 1e0ec88..42bed8c 100644 --- a/include/core/SkStrokeRec.h +++ b/include/core/SkStrokeRec.h @@ -21,6 +21,7 @@ public: SkStrokeRec(InitStyle style); SkStrokeRec(const SkStrokeRec&); + SkStrokeRec(const SkPaint&, SkPaint::Style); explicit SkStrokeRec(const SkPaint&); enum Style { @@ -90,6 +91,9 @@ public: } private: + void init(const SkPaint& paint, SkPaint::Style style); + + SkScalar fWidth; SkScalar fMiterLimit; SkPaint::Cap fCap; diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h index c909907..acf146f 100644 --- a/include/gpu/GrContext.h +++ b/include/gpu/GrContext.h @@ -15,6 +15,7 @@ #include "GrRenderTarget.h" #include "GrTexture.h" #include "SkMatrix.h" +#include "SkPathEffect.h" #include "SkTypes.h" class GrAARectRenderer; diff --git a/src/core/SkStrokeRec.cpp b/src/core/SkStrokeRec.cpp index ce744e5..a4c73af 100644 --- a/src/core/SkStrokeRec.cpp +++ b/src/core/SkStrokeRec.cpp @@ -24,7 +24,15 @@ SkStrokeRec::SkStrokeRec(const SkStrokeRec& src) { } SkStrokeRec::SkStrokeRec(const SkPaint& paint) { - switch (paint.getStyle()) { + this->init(paint, paint.getStyle()); +} + +SkStrokeRec::SkStrokeRec(const SkPaint& paint, SkPaint::Style styleOverride) { + this->init(paint, styleOverride); +} + +void SkStrokeRec::init(const SkPaint& paint, SkPaint::Style style) { + switch (style) { case SkPaint::kFill_Style: fWidth = kStrokeRec_FillStyleWidth; fStrokeAndFill = false; diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp index b64bf60..e823a98 100644 --- a/src/gpu/GrContext.cpp +++ b/src/gpu/GrContext.cpp @@ -9,8 +9,9 @@ #include "GrContext.h" -#include "effects/GrSingleTextureEffect.h" #include "effects/GrConfigConversionEffect.h" +#include "effects/GrDashingEffect.h" +#include "effects/GrSingleTextureEffect.h" #include "GrAARectRenderer.h" #include "GrBufferAllocPool.h" @@ -971,11 +972,10 @@ void GrContext::drawVertices(const GrPaint& paint, GrDrawTarget::AutoReleaseGeometry geo; // must be inside AutoCheckFlush scope GrDrawTarget* target = this->prepareToDraw(&paint, BUFFERED_DRAW, &are, &acf); + GrDrawState* drawState = target->drawState(); GR_CREATE_TRACE_MARKER("GrContext::drawVertices", target); - GrDrawState* drawState = target->drawState(); - int colorOffset = -1, texOffset = -1; set_vertex_attributes(drawState, texCoords, colors, &colorOffset, &texOffset); @@ -1168,6 +1168,24 @@ void GrContext::drawPath(const GrPaint& paint, const SkPath& path, const GrStrok } if (strokeInfo.isDashed()) { + SkPoint pts[2]; + if (path.isLine(pts)) { + AutoRestoreEffects are; + AutoCheckFlush acf(this); + GrDrawTarget* target = this->prepareToDraw(&paint, BUFFERED_DRAW, &are, &acf); + GrDrawState* drawState = target->drawState(); + + SkMatrix origViewMatrix = drawState->getViewMatrix(); + GrDrawState::AutoViewMatrixRestore avmr; + if (avmr.setIdentity(target->drawState())) { + if (GrDashingEffect::DrawDashLine(pts, paint, strokeInfo, fGpu, target, + origViewMatrix)) { + return; + } + } + } + + // Filter dashed path into new path with the dashing applied const SkPathEffect::DashInfo& info = strokeInfo.getDashInfo(); SkTLazy effectPath; GrStrokeInfo newStrokeInfo(strokeInfo, false); diff --git a/src/gpu/GrStrokeInfo.h b/src/gpu/GrStrokeInfo.h index b96ed39..b9ba5ea 100644 --- a/src/gpu/GrStrokeInfo.h +++ b/src/gpu/GrStrokeInfo.h @@ -33,10 +33,15 @@ public: } } + GrStrokeInfo(const SkPaint& paint, SkPaint::Style styleOverride) : + fStroke(paint, styleOverride), fDashType(SkPathEffect::kNone_DashType) { + this->init(paint); + } + + explicit GrStrokeInfo(const SkPaint& paint) : fStroke(paint), fDashType(SkPathEffect::kNone_DashType) { - const SkPathEffect* pe = paint.getPathEffect(); - this->setDashInfo(pe); + this->init(paint); } const SkStrokeRec& getStrokeRec() const { return fStroke; } @@ -79,6 +84,12 @@ public: const SkPathEffect::DashInfo& getDashInfo() const { return fDashInfo; } private: + + void init(const SkPaint& paint) { + const SkPathEffect* pe = paint.getPathEffect(); + this->setDashInfo(pe); + } + SkStrokeRec fStroke; SkPathEffect::DashType fDashType; SkPathEffect::DashInfo fDashInfo; diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index 288ea5a..11a79db 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -386,9 +386,14 @@ void SkGpuDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, } if (paint.getPathEffect() && 2 == count && SkCanvas::kLines_PointMode == mode) { - if (GrDashingEffect::DrawDashLine(pts, paint, this->context())) { - return; - } + GrStrokeInfo strokeInfo(paint, SkPaint::kStroke_Style); + GrPaint grPaint; + SkPaint2GrPaintShader(this->context(), paint, true, &grPaint); + SkPath path; + path.moveTo(pts[0]); + path.lineTo(pts[1]); + fContext->drawPath(grPaint, path, strokeInfo); + return; } // we only handle hairlines and paints without path effects or mask filters, diff --git a/src/gpu/effects/GrDashingEffect.cpp b/src/gpu/effects/GrDashingEffect.cpp index 3b06c3a..8d9c08f 100644 --- a/src/gpu/effects/GrDashingEffect.cpp +++ b/src/gpu/effects/GrDashingEffect.cpp @@ -7,17 +7,73 @@ #include "GrDashingEffect.h" +#include "../GrAARectRenderer.h" + +#include "effects/GrVertexEffect.h" #include "gl/GrGLEffect.h" +#include "gl/GrGLVertexEffect.h" #include "gl/GrGLSL.h" #include "GrContext.h" #include "GrCoordTransform.h" +#include "GrDrawTarget.h" #include "GrDrawTargetCaps.h" #include "GrEffect.h" +#include "GrGpu.h" +#include "GrStrokeInfo.h" #include "GrTBackendEffectFactory.h" #include "SkGr.h" /////////////////////////////////////////////////////////////////////////////// +// Returns whether or not the gpu can fast path the dash line effect. +static bool can_fast_path_dash(const SkPoint pts[2], const GrStrokeInfo& strokeInfo, + const GrDrawTarget& target, const SkMatrix& viewMatrix) { + if (target.getDrawState().getRenderTarget()->isMultisampled()) { + return false; + } + + // Pts must be either horizontal or vertical in src space + if (pts[0].fX != pts[1].fX && pts[0].fY != pts[1].fY) { + return false; + } + + // May be able to relax this to include skew. As of now cannot do perspective + // because of the non uniform scaling of bloating a rect + if (!viewMatrix.preservesRightAngles()) { + return false; + } + + if (!strokeInfo.isDashed() || 2 != strokeInfo.dashCount()) { + return false; + } + + const SkPathEffect::DashInfo& info = strokeInfo.getDashInfo(); + if (0 == info.fIntervals[0] && 0 == info.fIntervals[1]) { + return false; + } + + SkPaint::Cap cap = strokeInfo.getStrokeRec().getCap(); + // Current we do don't handle Round or Square cap dashes + if (SkPaint::kRound_Cap == cap) { + return false; + } + + return true; +} + +namespace { + +struct DashLineVertex { + SkPoint fPos; + SkPoint fDashPos; +}; + +extern const GrVertexAttrib gDashLineVertexAttribs[] = { + { kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding }, + { kVec2f_GrVertexAttribType, sizeof(SkPoint), kEffect_GrVertexAttribBinding }, +}; + +}; static void calc_dash_scaling(SkScalar* parallelScale, SkScalar* perpScale, const SkMatrix& viewMatrix, const SkPoint pts[2]) { SkVector vecSrc = pts[1] - pts[0]; @@ -62,7 +118,8 @@ static SkScalar calc_start_adjustment(const SkPathEffect::DashInfo& info) { return 0; } -static SkScalar calc_end_adjustment(const SkPathEffect::DashInfo& info, const SkPoint pts[2], SkScalar* endingInt) { +static SkScalar calc_end_adjustment(const SkPathEffect::DashInfo& info, const SkPoint pts[2], + SkScalar phase, SkScalar* endingInt) { if (pts[1].fX <= pts[0].fX) { return 0; } @@ -70,7 +127,7 @@ static SkScalar calc_end_adjustment(const SkPathEffect::DashInfo& info, const Sk SkScalar totalLen = pts[1].fX - pts[0].fX; SkScalar temp = SkScalarDiv(totalLen, srcIntervalLen); SkScalar numFullIntervals = SkScalarFloorToScalar(temp); - *endingInt = totalLen - numFullIntervals * srcIntervalLen + info.fPhase; + *endingInt = totalLen - numFullIntervals * srcIntervalLen + phase; temp = SkScalarDiv(*endingInt, srcIntervalLen); *endingInt = *endingInt - SkScalarFloorToScalar(temp) * srcIntervalLen; if (0 == *endingInt) { @@ -85,188 +142,263 @@ static SkScalar calc_end_adjustment(const SkPathEffect::DashInfo& info, const Sk return 0; } +static void setup_dashed_rect(const SkRect& rect, DashLineVertex* verts, int idx, const SkMatrix& matrix, + SkScalar offset, SkScalar bloat, SkScalar len, SkScalar stroke) { -bool GrDashingEffect::DrawDashLine(const SkPoint pts[2], const SkPaint& paint, GrContext* context) { - if (context->getRenderTarget()->isMultisampled()) { - return false; - } + SkScalar startDashX = offset - bloat; + SkScalar endDashX = offset + len + bloat; + SkScalar startDashY = -stroke - bloat; + SkScalar endDashY = stroke + bloat; + verts[idx].fDashPos = SkPoint::Make(startDashX , startDashY); + verts[idx + 1].fDashPos = SkPoint::Make(startDashX, endDashY); + verts[idx + 2].fDashPos = SkPoint::Make(endDashX, endDashY); + verts[idx + 3].fDashPos = SkPoint::Make(endDashX, startDashY); - const SkMatrix& viewMatrix = context->getMatrix(); - if (!viewMatrix.preservesRightAngles()) { - return false; - } + verts[idx].fPos = SkPoint::Make(rect.fLeft, rect.fTop); + verts[idx + 1].fPos = SkPoint::Make(rect.fLeft, rect.fBottom); + verts[idx + 2].fPos = SkPoint::Make(rect.fRight, rect.fBottom); + verts[idx + 3].fPos = SkPoint::Make(rect.fRight, rect.fTop); - const SkPathEffect* pe = paint.getPathEffect(); - SkPathEffect::DashInfo info; - SkPathEffect::DashType dashType = pe->asADash(&info); - // Must be a dash effect with 2 intervals (1 on and 1 off) - if (SkPathEffect::kDash_DashType != dashType || 2 != info.fCount) { - return false; - } + matrix.mapPointsWithStride(&verts[idx].fPos, sizeof(DashLineVertex), 4); +} - SkPaint::Cap cap = paint.getStrokeCap(); - // Current we do don't handle Round or Square cap dashes - if (SkPaint::kRound_Cap == cap) { + +bool GrDashingEffect::DrawDashLine(const SkPoint pts[2], const GrPaint& paint, + const GrStrokeInfo& strokeInfo, GrGpu* gpu, + GrDrawTarget* target, const SkMatrix& vm) { + + if (!can_fast_path_dash(pts, strokeInfo, *target, vm)) { return false; } - SkScalar srcStrokeWidth = paint.getStrokeWidth(); + const SkPathEffect::DashInfo& info = strokeInfo.getDashInfo(); - // Get all info about the dash effect - SkAutoTArray intervals(info.fCount); - info.fIntervals = intervals.get(); - pe->asADash(&info); + SkPaint::Cap cap = strokeInfo.getStrokeRec().getCap(); + + SkScalar srcStrokeWidth = strokeInfo.getStrokeRec().getWidth(); // the phase should be normalized to be [0, sum of all intervals) SkASSERT(info.fPhase >= 0 && info.fPhase < info.fIntervals[0] + info.fIntervals[1]); - SkMatrix coordTrans; + SkScalar srcPhase = info.fPhase; // 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) { - align_to_x_axis(pts, &coordTrans, ptsRot); - if(!coordTrans.invert(&srcRotInv)) { + SkMatrix rotMatrix; + align_to_x_axis(pts, &rotMatrix, ptsRot); + if(!rotMatrix.invert(&srcRotInv)) { + GrPrintf("Failed to create invertible rotation matrix!\n"); return false; } } else { - coordTrans.reset(); srcRotInv.reset(); memcpy(ptsRot, pts, 2 * sizeof(SkPoint)); } - GrPaint grPaint; - SkPaint2GrPaintShader(context, paint, true, &grPaint); - bool useAA = paint.isAntiAlias(); // Scale corrections of intervals and stroke from view matrix SkScalar parallelScale; SkScalar perpScale; - calc_dash_scaling(¶llelScale, &perpScale, viewMatrix, ptsRot); + calc_dash_scaling(¶llelScale, &perpScale, vm, ptsRot); bool hasCap = SkPaint::kSquare_Cap == cap && 0 != srcStrokeWidth; // 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 halfStroke = SkMaxScalar(srcStrokeWidth * 0.5f, 0.5f / perpScale); + SkScalar halfSrcStroke = SkMaxScalar(srcStrokeWidth * 0.5f, 0.5f / perpScale); - SkScalar xStroke; + SkScalar strokeAdj; if (!hasCap) { - xStroke = 0.f; + strokeAdj = 0.f; } else { - xStroke = halfStroke; + strokeAdj = halfSrcStroke; } + SkScalar startAdj = 0; + + SkMatrix combinedMatrix = srcRotInv; + combinedMatrix.postConcat(vm); + + 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 (info.fPhase > 0 && info.fPhase < info.fIntervals[0]) { + 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] - info.fPhase, + startPts[1].fX = SkMinScalar(startPts[0].fX + info.fIntervals[0] - srcPhase, ptsRot[1].fX); - SkRect startRect; startRect.set(startPts, 2); - startRect.outset(xStroke, halfStroke); - context->drawRect(grPaint, startRect, NULL, &srcRotInv); + startRect.outset(strokeAdj, halfSrcStroke); - ptsRot[0].fX += info.fIntervals[0] + info.fIntervals[1] - info.fPhase; - info.fPhase = 0; + hasStartRect = true; + startAdj = info.fIntervals[0] + info.fIntervals[1] - srcPhase; } } // adjustments for start and end of bounding rect so we only draw dash intervals // contained in the original line segment. - SkScalar startAdj = calc_start_adjustment(info); + startAdj += calc_start_adjustment(info); + if (startAdj != 0) { + ptsRot[0].fX += startAdj; + srcPhase = 0; + } SkScalar endingInterval = 0; - SkScalar endAdj = calc_end_adjustment(info, ptsRot, &endingInterval); - if (ptsRot[0].fX + startAdj >= ptsRot[1].fX - endAdj) { - // Nothing left to draw so just return - return true; + SkScalar endAdj = calc_end_adjustment(info, ptsRot, srcPhase, &endingInterval); + ptsRot[1].fX -= endAdj; + if (ptsRot[0].fX >= ptsRot[1].fX) { + lineDone = true; } + 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) { + 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; - SkRect endRect; endRect.set(endPts, 2); - endRect.outset(xStroke, halfStroke); - context->drawRect(grPaint, endRect, NULL, &srcRotInv); + endRect.outset(strokeAdj, halfSrcStroke); + + hasEndRect = true; + endAdj = endingInterval + info.fIntervals[1]; - ptsRot[1].fX -= endingInterval + info.fIntervals[1]; + ptsRot[1].fX -= endAdj; if (ptsRot[0].fX >= ptsRot[1].fX) { - // Nothing left to draw so just return - return true; + lineDone = true; } } } - coordTrans.postConcat(viewMatrix); - SkPoint devicePts[2]; - viewMatrix.mapPoints(devicePts, ptsRot, 2); + if (startAdj != 0) { + srcPhase = 0; + } - info.fIntervals[0] *= parallelScale; - info.fIntervals[1] *= parallelScale; - info.fPhase *= parallelScale; + // 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; if ((strokeWidth < 1.f && !useAA) || 0.f == strokeWidth) { strokeWidth = 1.f; } - // Set up coordTransform for device space transforms - // We rotate the dashed line such that it is horizontal with the start point at smaller x - // then we translate the start point to the origin - if (devicePts[0].fY != devicePts[1].fY || devicePts[0].fX > devicePts[1].fX) { - SkMatrix rot; - align_to_x_axis(devicePts, &rot); - coordTrans.postConcat(rot); - } - coordTrans.postTranslate(-devicePts[0].fX, -devicePts[0].fY); - coordTrans.postTranslate(info.fIntervals[1] * 0.5f + info.fPhase, 0); + SkScalar halfDevStroke = strokeWidth * 0.5f; if (SkPaint::kSquare_Cap == cap && 0 != srcStrokeWidth) { // add cap to on interveal and remove from off interval - info.fIntervals[0] += strokeWidth; - info.fIntervals[1] -= strokeWidth; + devIntervals[0] += strokeWidth; + devIntervals[1] -= strokeWidth; } - - if (info.fIntervals[1] > 0.f) { + 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; + + GrDrawState* drawState = target->drawState(); + 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]; + vm.mapPoints(devicePts, ptsRot, 2); + SkScalar lineLength = SkPoint::Distance(devicePts[0], devicePts[1]); + if (hasCap) { + lineLength += 2.f * halfDevStroke; + } + devIntervals[0] = lineLength; + } + if (devIntervals[1] > 0.f || useAA) { + SkPathEffect::DashInfo devInfo; + devInfo.fPhase = devPhase; + devInfo.fCount = 2; + devInfo.fIntervals = devIntervals; GrEffectEdgeType edgeType= useAA ? kFillAA_GrEffectEdgeType : kFillBW_GrEffectEdgeType; - grPaint.addCoverageEffect( - GrDashingEffect::Create(edgeType, info, coordTrans, strokeWidth))->unref(); - grPaint.setAntiAlias(false); + drawState->addCoverageEffect( + GrDashingEffect::Create(edgeType, devInfo, strokeWidth), 1)->unref(); } - SkRect rect; - bool bloat = useAA && info.fIntervals[1] > 0.f; - SkScalar bloatX = bloat ? 0.5f / parallelScale : 0.f; - SkScalar bloatY = bloat ? 0.5f / perpScale : 0.f; - ptsRot[0].fX += startAdj; - ptsRot[1].fX -= endAdj; - if (!hasCap) { - xStroke = 0.f; - } else { - xStroke = halfStroke; + // Set up the vertex data for the line and start/end dashes + drawState->setVertexAttribs(SK_ARRAY_COUNT(gDashLineVertexAttribs)); + + int totalRectCnt = 0; + + totalRectCnt += !lineDone ? 1 : 0; + totalRectCnt += hasStartRect ? 1 : 0; + totalRectCnt += hasEndRect ? 1 : 0; + + GrDrawTarget::AutoReleaseGeometry geo(target, totalRectCnt * 4, 0); + if (!geo.succeeded()) { + GrPrintf("Failed to get space for vertices!\n"); + return false; } - rect.set(ptsRot, 2); - rect.outset(bloatX + xStroke, bloatY + halfStroke); - context->drawRect(grPaint, rect, NULL, &srcRotInv); + DashLineVertex* verts = reinterpret_cast(geo.vertices()); + + int curVIdx = 0; + + // Draw interior part of dashed line + if (!lineDone) { + SkPoint devicePts[2]; + vm.mapPoints(devicePts, ptsRot, 2); + SkScalar lineLength = SkPoint::Distance(devicePts[0], devicePts[1]); + if (hasCap) { + lineLength += 2.f * halfDevStroke; + } + + SkRect bounds; + bounds.set(ptsRot[0].fX, ptsRot[0].fY, ptsRot[1].fX, ptsRot[1].fY); + bounds.outset(bloatX + strokeAdj, bloatY + halfSrcStroke); + setup_dashed_rect(bounds, verts, curVIdx, combinedMatrix, startOffset, devBloat, + lineLength, halfDevStroke); + curVIdx += 4; + } + + if (hasStartRect) { + SkASSERT(useAA); // so that we know bloatX and bloatY have been set + startRect.outset(bloatX, bloatY); + setup_dashed_rect(startRect, verts, curVIdx, combinedMatrix, startOffset, devBloat, + devIntervals[0], halfDevStroke); + curVIdx += 4; + } + + if (hasEndRect) { + SkASSERT(useAA); // so that we know bloatX and bloatY have been set + endRect.outset(bloatX, bloatY); + setup_dashed_rect(endRect, verts, curVIdx, combinedMatrix, startOffset, devBloat, + devIntervals[0], halfDevStroke); + } + + target->setIndexSourceToBuffer(gpu->getContext()->getQuadIndexBuffer()); + target->drawIndexedInstances(kTriangles_GrPrimitiveType, totalRectCnt, 4, 6); + target->resetIndexSource(); return true; } @@ -274,7 +406,7 @@ bool GrDashingEffect::DrawDashLine(const SkPoint pts[2], const SkPaint& paint, G class GLDashingLineEffect; -class DashingLineEffect : public GrEffect { +class DashingLineEffect : public GrVertexEffect { public: typedef SkPathEffect::DashInfo DashInfo; @@ -286,7 +418,7 @@ public: * and half the off interval. */ static GrEffectRef* Create(GrEffectEdgeType edgeType, const DashInfo& info, - const SkMatrix& matrix, SkScalar strokeWidth); + SkScalar strokeWidth); virtual ~DashingLineEffect(); @@ -305,13 +437,11 @@ public: virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE; private: - DashingLineEffect(GrEffectEdgeType edgeType, const DashInfo& info, const SkMatrix& matrix, - SkScalar strokeWidth); + DashingLineEffect(GrEffectEdgeType edgeType, const DashInfo& info, SkScalar strokeWidth); virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE; GrEffectEdgeType fEdgeType; - GrCoordTransform fCoordTransform; SkRect fRect; SkScalar fIntervalLength; @@ -322,11 +452,11 @@ private: ////////////////////////////////////////////////////////////////////////////// -class GLDashingLineEffect : public GrGLEffect { +class GLDashingLineEffect : public GrGLVertexEffect { public: GLDashingLineEffect(const GrBackendEffectFactory&, const GrDrawEffect&); - virtual void emitCode(GrGLShaderBuilder* builder, + virtual void emitCode(GrGLFullShaderBuilder* builder, const GrDrawEffect& drawEffect, EffectKey key, const char* outputColor, @@ -343,7 +473,7 @@ private: GrGLUniformManager::UniformHandle fIntervalUniform; SkRect fPrevRect; SkScalar fPrevIntervalLength; - typedef GrGLEffect INHERITED; + typedef GrGLVertexEffect INHERITED; }; GLDashingLineEffect::GLDashingLineEffect(const GrBackendEffectFactory& factory, @@ -354,12 +484,12 @@ GLDashingLineEffect::GLDashingLineEffect(const GrBackendEffectFactory& factory, } -void GLDashingLineEffect::emitCode(GrGLShaderBuilder* builder, +void GLDashingLineEffect::emitCode(GrGLFullShaderBuilder* builder, const GrDrawEffect& drawEffect, EffectKey key, const char* outputColor, const char* inputColor, - const TransformedCoordsArray& coords, + const TransformedCoordsArray&, const TextureSamplerArray& samplers) { const DashingLineEffect& de = drawEffect.castEffect(); const char *rectName; @@ -375,10 +505,17 @@ void GLDashingLineEffect::emitCode(GrGLShaderBuilder* builder, kFloat_GrSLType, "interval", &intervalName); + + const char *vsCoordName, *fsCoordName; + builder->addVarying(kVec2f_GrSLType, "Coord", &vsCoordName, &fsCoordName); + const SkString* attr0Name = + builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]); + builder->vsCodeAppendf("\t%s = %s;\n", vsCoordName, attr0Name->c_str()); + // transforms all points so that we can compare them to our test rect builder->fsCodeAppendf("\t\tfloat xShifted = %s.x - floor(%s.x / %s) * %s;\n", - coords[0].c_str(), coords[0].c_str(), intervalName, intervalName); - builder->fsCodeAppendf("\t\tvec2 fragPosShifted = vec2(xShifted, %s.y);\n", coords[0].c_str()); + fsCoordName, fsCoordName, intervalName, intervalName); + builder->fsCodeAppendf("\t\tvec2 fragPosShifted = vec2(xShifted, %s.y);\n", fsCoordName); if (GrEffectEdgeTypeIsAA(de.getEdgeType())) { // The amount of coverage removed in x and y by the edges is computed as a pair of negative // numbers, xSub and ySub. @@ -422,13 +559,13 @@ GrGLEffect::EffectKey GLDashingLineEffect::GenKey(const GrDrawEffect& drawEffect ////////////////////////////////////////////////////////////////////////////// GrEffectRef* DashingLineEffect::Create(GrEffectEdgeType edgeType, const DashInfo& info, - const SkMatrix& matrix, SkScalar strokeWidth) { + SkScalar strokeWidth) { if (info.fCount != 2) { return NULL; } return CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(DashingLineEffect, - (edgeType, info, matrix, strokeWidth)))); + (edgeType, info, strokeWidth)))); } DashingLineEffect::~DashingLineEffect() {} @@ -442,9 +579,8 @@ const GrBackendEffectFactory& DashingLineEffect::getFactory() const { } DashingLineEffect::DashingLineEffect(GrEffectEdgeType edgeType, const DashInfo& info, - const SkMatrix& matrix, SkScalar strokeWidth) - : fEdgeType(edgeType) - , fCoordTransform(kLocal_GrCoordSet, matrix) { + SkScalar strokeWidth) + : fEdgeType(edgeType) { SkScalar onLen = info.fIntervals[0]; SkScalar offLen = info.fIntervals[1]; SkScalar halfOffLen = SkScalarHalf(offLen); @@ -452,13 +588,12 @@ DashingLineEffect::DashingLineEffect(GrEffectEdgeType edgeType, const DashInfo& fIntervalLength = onLen + offLen; fRect.set(halfOffLen, -halfStroke, halfOffLen + onLen, halfStroke); - addCoordTransform(&fCoordTransform); + this->addVertexAttrib(kVec2f_GrSLType); } bool DashingLineEffect::onIsEqual(const GrEffect& other) const { const DashingLineEffect& de = CastEffect(other); return (fEdgeType == de.fEdgeType && - fCoordTransform == de.fCoordTransform && fRect == de.fRect && fIntervalLength == de.fIntervalLength); } @@ -470,8 +605,6 @@ GrEffectRef* DashingLineEffect::TestCreate(SkRandom* random, const GrDrawTargetCaps& caps, GrTexture*[]) { GrEffectRef* effect; - SkMatrix m; - m.reset(); GrEffectEdgeType edgeType = static_cast(random->nextULessThan( kGrEffectEdgeTypeCnt)); SkScalar strokeWidth = random->nextRangeScalar(0, 100.f); @@ -483,13 +616,13 @@ GrEffectRef* DashingLineEffect::TestCreate(SkRandom* random, info.fIntervals[1] = random->nextRangeScalar(0, 10.f); info.fPhase = random->nextRangeScalar(0, info.fIntervals[0] + info.fIntervals[1]); - effect = DashingLineEffect::Create(edgeType, info, m, strokeWidth); + effect = DashingLineEffect::Create(edgeType, info, strokeWidth); return effect; } ////////////////////////////////////////////////////////////////////////////// GrEffectRef* GrDashingEffect::Create(GrEffectEdgeType edgeType, const SkPathEffect::DashInfo& info, - const SkMatrix& matrix, SkScalar strokeWidth) { - return DashingLineEffect::Create(edgeType, info, matrix, strokeWidth); + SkScalar strokeWidth) { + return DashingLineEffect::Create(edgeType, info, strokeWidth); } diff --git a/src/gpu/effects/GrDashingEffect.h b/src/gpu/effects/GrDashingEffect.h index 0ed1cf0..8096017 100644 --- a/src/gpu/effects/GrDashingEffect.h +++ b/src/gpu/effects/GrDashingEffect.h @@ -12,13 +12,17 @@ #include "GrTypesPriv.h" #include "SkPathEffect.h" -class GrContext; +class GrGpu; +class GrDrawTarget; +class GrPaint; +class GrStrokeInfo; class GrGLDashingEffect; class SkPath; namespace GrDashingEffect { - bool DrawDashLine(const SkPoint pnts[2], const SkPaint& paint, GrContext* context); + bool DrawDashLine(const SkPoint pts[2], const GrPaint& paint, const GrStrokeInfo& strokeInfo, + GrGpu* gpu, GrDrawTarget* target, const SkMatrix& vm); /** * An effect that renders a dashed line. It is intended to be used as a coverage effect. @@ -27,7 +31,7 @@ namespace GrDashingEffect { * position relative to the dashed line. */ GrEffectRef* Create(GrEffectEdgeType edgeType, const SkPathEffect::DashInfo& info, - const SkMatrix& matrix, SkScalar strokeWidth); + SkScalar strokeWidth); } #endif -- 2.7.4