From: commit-bot@chromium.org Date: Mon, 19 May 2014 14:32:49 +0000 (+0000) Subject: Add Dashing gpu effect for simple dashed lines X-Git-Tag: accepted/tizen/5.0/unified/20181102.025319~7688 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=628ed0b22045a9544b56e00e73137d6050ce900c;p=platform%2Fupstream%2FlibSkiaSharp.git Add Dashing gpu effect for simple dashed lines BUG=skia: R=bsalomon@google.com Author: egdaniel@google.com Review URL: https://codereview.chromium.org/274673004 git-svn-id: http://skia.googlecode.com/svn/trunk@14775 2bbb7eff-a529-9590-31e7-b0007b416f81 --- diff --git a/expectations/gm/ignored-tests.txt b/expectations/gm/ignored-tests.txt index 9afd2ef..4edbcc2 100644 --- a/expectations/gm/ignored-tests.txt +++ b/expectations/gm/ignored-tests.txt @@ -41,3 +41,10 @@ canvas-layer-state # bsalomon: https://codereview.chromium.org/264303008/ # bsalomon@ will rebaseline this test ninepatch-stretch + +# egdaniel: https://codereview.chromium.org/274673004/ +# dashing effects will change slightly on GPU, will rebaseline after patch lands +dashing +dashing2 +dashing3 +dashcubics diff --git a/gm/dashing.cpp b/gm/dashing.cpp index 2f5be08..23de24d 100644 --- a/gm/dashing.cpp +++ b/gm/dashing.cpp @@ -11,7 +11,8 @@ #include "SkDashPathEffect.h" static void drawline(SkCanvas* canvas, int on, int off, const SkPaint& paint, - SkScalar finalX = SkIntToScalar(600)) { + SkScalar finalX = SkIntToScalar(600), SkScalar finalY = SkIntToScalar(0), + SkScalar phase = SkIntToScalar(0)) { SkPaint p(paint); const SkScalar intervals[] = { @@ -19,8 +20,8 @@ static void drawline(SkCanvas* canvas, int on, int off, const SkPaint& paint, SkIntToScalar(off), }; - p.setPathEffect(SkDashPathEffect::Create(intervals, 2, 0))->unref(); - canvas->drawLine(0, 0, finalX, 0, p); + p.setPathEffect(SkDashPathEffect::Create(intervals, 2, phase))->unref(); + canvas->drawLine(0, 0, finalX, finalY, p); } // earlier bug stopped us from drawing very long single-segment dashes, because @@ -233,6 +234,7 @@ protected: canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, p); } + } virtual void onDraw(SkCanvas* canvas) { @@ -313,10 +315,96 @@ protected: ////////////////////////////////////////////////////////////////////////////// +class Dashing4GM : public skiagm::GM { +public: + Dashing4GM() {} + +protected: + virtual uint32_t onGetFlags() const SK_OVERRIDE { + return kSkipTiled_Flag; + } + + SkString onShortName() { + return SkString("dashing4"); + } + + SkISize onISize() { return skiagm::make_isize(640, 950); } + + virtual void onDraw(SkCanvas* canvas) { + static const struct { + int fOnInterval; + int fOffInterval; + } gData[] = { + { 1, 1 }, + { 4, 2 }, + { 0, 4 }, // test for zero length on interval + }; + + SkPaint paint; + paint.setStyle(SkPaint::kStroke_Style); + + 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) { + for (int cap = 0; cap <= 1; ++cap) { + int w = width * width * width; + paint.setAntiAlias(SkToBool(aa)); + paint.setStrokeWidth(SkIntToScalar(w)); + + SkToBool(cap) ? paint.setStrokeCap(SkPaint::kSquare_Cap) + : paint.setStrokeCap(SkPaint::kRound_Cap); + + int scale = w ? w : 1; + + drawline(canvas, gData[data].fOnInterval * scale, + gData[data].fOffInterval * scale, + paint); + canvas->translate(0, SkIntToScalar(20)); + } + } + } + } + + for (int aa = 0; aa <= 1; ++aa) { + 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.f, 16.f, paint, 20.f, 0, 5.f); + canvas->translate(0, SkIntToScalar(20)); + + // Two dash elements where each one is cut off at beginning and end respectively + drawline(canvas, 32.f, 16.f, paint, 56.f, 0, 5.f); + canvas->translate(0, SkIntToScalar(20)); + + // Many dash elements where first and last are cut off at beginning and end respectively + drawline(canvas, 32.f, 16.f, paint, 584.f, 0, 5.f); + canvas->translate(0, SkIntToScalar(20)); + + // Diagonal dash line where src pnts are not axis aligned (as apposed to being diagonal from + // a canvas rotation) + drawline(canvas, 32.f, 16.f, paint, 600.f, 30.f); + canvas->translate(0, SkIntToScalar(20)); + + // Case where only the off interval exists on the line. Thus nothing should be drawn + drawline(canvas, 32.f, 16.f, paint, 8.f, 0.f, 40.f); + canvas->translate(0, SkIntToScalar(20)); + } + } +}; + +////////////////////////////////////////////////////////////////////////////// + static skiagm::GM* F0(void*) { return new DashingGM; } static skiagm::GM* F1(void*) { return new Dashing2GM; } static skiagm::GM* F2(void*) { return new Dashing3GM; } +static skiagm::GM* F3(void*) { return new Dashing4GM; } static skiagm::GMRegistry gR0(F0); static skiagm::GMRegistry gR1(F1); static skiagm::GMRegistry gR2(F2); +static skiagm::GMRegistry gR3(F3); diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi index 258b532..0410688 100644 --- a/gyp/gpu.gypi +++ b/gyp/gpu.gypi @@ -150,6 +150,8 @@ '<(skia_src_path)/gpu/effects/GrBicubicEffect.h', '<(skia_src_path)/gpu/effects/GrCustomCoordsTextureEffect.cpp', '<(skia_src_path)/gpu/effects/GrCustomCoordsTextureEffect.h', + '<(skia_src_path)/gpu/effects/GrDashingEffect.cpp', + '<(skia_src_path)/gpu/effects/GrDashingEffect.h', '<(skia_src_path)/gpu/effects/GrDistanceFieldTextureEffect.cpp', '<(skia_src_path)/gpu/effects/GrDistanceFieldTextureEffect.h', '<(skia_src_path)/gpu/effects/GrOvalEffect.cpp', diff --git a/include/gpu/SkGpuDevice.h b/include/gpu/SkGpuDevice.h index 15951aa..d2df32c 100644 --- a/include/gpu/SkGpuDevice.h +++ b/include/gpu/SkGpuDevice.h @@ -219,6 +219,8 @@ private: int tileSize, bool bicubic); + bool drawDashLine(const SkPoint pts[2], const SkPaint& paint); + static SkPicture::AccelData::Key ComputeAccelDataKey(); typedef SkBitmapDevice INHERITED; diff --git a/samplecode/SampleApp.cpp b/samplecode/SampleApp.cpp index 8003a06..473220e 100644 --- a/samplecode/SampleApp.cpp +++ b/samplecode/SampleApp.cpp @@ -78,7 +78,7 @@ public: SkTCPServer gServer; #endif -#define USE_ARROWS_FOR_ZOOM false +#define USE_ARROWS_FOR_ZOOM true #if SK_ANGLE //#define DEFAULT_TO_ANGLE 1 diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index 6543139..30cae78 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -8,6 +8,7 @@ #include "SkGpuDevice.h" #include "effects/GrBicubicEffect.h" +#include "effects/GrDashingEffect.h" #include "effects/GrTextureDomain.h" #include "effects/GrSimpleTextureEffect.h" @@ -419,6 +420,12 @@ void SkGpuDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, return; } + if (paint.getPathEffect() && 2 == count && SkCanvas::kLines_PointMode == mode) { + if (GrDashingEffect::DrawDashLine(pts, paint, this)) { + return; + } + } + // we only handle hairlines and paints without path effects or mask filters, // else we let the SkDraw call our drawPath() if (width > 0 || paint.getPathEffect() || paint.getMaskFilter()) { diff --git a/src/gpu/effects/GrDashingEffect.cpp b/src/gpu/effects/GrDashingEffect.cpp new file mode 100644 index 0000000..8a7ce01 --- /dev/null +++ b/src/gpu/effects/GrDashingEffect.cpp @@ -0,0 +1,497 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrDashingEffect.h" + +#include "gl/GrGLEffect.h" +#include "gl/GrGLSL.h" +#include "GrContext.h" +#include "GrCoordTransform.h" +#include "GrDrawTargetCaps.h" +#include "GrEffect.h" +#include "GrTBackendEffectFactory.h" +#include "SkGpuDevice.h" +#include "SkGr.h" + +/////////////////////////////////////////////////////////////////////////////// + +static void calc_dash_scaling(SkScalar* parallelScale, SkScalar* perpScale, + const SkMatrix& viewMatrix, const SkPoint pts[2]) { + SkVector vecSrc = pts[1] - pts[0]; + SkScalar magSrc = vecSrc.length(); + SkScalar invSrc = magSrc ? SkScalarInvert(magSrc) : 0; + vecSrc.scale(invSrc); + + SkVector vecSrcPerp; + vecSrc.rotateCW(&vecSrcPerp); + viewMatrix.mapVectors(&vecSrc, 1); + viewMatrix.mapVectors(&vecSrcPerp, 1); + + // parallelScale tells how much to scale along the line parallel to the dash line + // perpScale tells how much to scale in the direction perpendicular to the dash line + *parallelScale = vecSrc.length(); + *perpScale = vecSrcPerp.length(); +} + +// calculates the rotation needed to aligned pts to the x axis with pts[0] < pts[1] +// Stores the rotation matrix in rotMatrix, and the mapped points in ptsRot +static void align_to_x_axis(const SkPoint pts[2], SkMatrix* rotMatrix, SkPoint ptsRot[2] = NULL) { + SkVector vec = pts[1] - pts[0]; + SkScalar mag = vec.length(); + SkScalar inv = mag ? SkScalarInvert(mag) : 0; + + vec.scale(inv); + rotMatrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY); + if (ptsRot) { + rotMatrix->mapPoints(ptsRot, pts, 2); + // correction for numerical issues if map doesn't make ptsRot exactly horizontal + ptsRot[1].fY = pts[0].fY; + } +} + +// 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; + } + return 0; +} + +static SkScalar calc_end_adjustment(const SkPathEffect::DashInfo& info, const SkPoint pts[2], SkScalar* endingInt) { + if (pts[1].fX <= pts[0].fX) { + return 0; + } + SkScalar srcIntervalLen = info.fIntervals[0] + info.fIntervals[1]; + SkScalar totalLen = pts[1].fX - pts[0].fX; + SkScalar temp = SkScalarDiv(totalLen, srcIntervalLen); + SkScalar numFullIntervals = SkScalarFloorToScalar(temp); + *endingInt = totalLen - numFullIntervals * srcIntervalLen + info.fPhase; + temp = SkScalarDiv(*endingInt, srcIntervalLen); + *endingInt = *endingInt - SkScalarFloorToScalar(temp) * srcIntervalLen; + if (0 == *endingInt) { + *endingInt = srcIntervalLen; + } + if (*endingInt > info.fIntervals[0]) { + if (0 == info.fIntervals[0]) { + *endingInt -= 0.01; // make sure we capture the last zero size pnt (used if has caps) + } + return *endingInt - info.fIntervals[0]; + } + return 0; +} + + +bool GrDashingEffect::DrawDashLine(const SkPoint pts[2], const SkPaint& paint, SkGpuDevice* dev) { + GrContext* context = dev->context(); + if (context->getRenderTarget()->isMultisampled()) { + return false; + } + + const SkMatrix& viewMatrix = context->getMatrix(); + if (!viewMatrix.preservesRightAngles()) { + return false; + } + + 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; + } + + SkPaint::Cap cap = paint.getStrokeCap(); + // Current we do don't handle Round or Square cap dashes + if (SkPaint::kRound_Cap == cap) { + return false; + } + + SkScalar srcStrokeWidth = paint.getStrokeWidth(); + + // Get all info about the dash effect + SkAutoTArray intervals(info.fCount); + info.fIntervals = intervals.get(); + pe->asADash(&info); + + // 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; + + // 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)) { + return false; + } + } else { + coordTrans.reset(); + srcRotInv.reset(); + memcpy(ptsRot, pts, 2 * sizeof(SkPoint)); + } + + GrPaint grPaint; + SkPaint2GrPaintShader(dev, 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); + + 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 xStroke; + if (!hasCap) { + xStroke = 0.f; + } else { + xStroke = halfStroke; + } + + // 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]) { + 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, + ptsRot[1].fX); + SkRect startRect; + startRect.set(startPts, 2); + startRect.outset(xStroke, halfStroke); + context->drawRect(grPaint, startRect, NULL, &srcRotInv); + + ptsRot[0].fX += info.fIntervals[0] + info.fIntervals[1] - info.fPhase; + info.fPhase = 0; + } + } + + // 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); + 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; + } + + // 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 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); + + ptsRot[1].fX -= endingInterval + info.fIntervals[1]; + if (ptsRot[0].fX >= ptsRot[1].fX) { + // Nothing left to draw so just return + return true; + } + } + } + coordTrans.postConcat(viewMatrix); + + SkPoint devicePts[2]; + viewMatrix.mapPoints(devicePts, ptsRot, 2); + + info.fIntervals[0] *= parallelScale; + info.fIntervals[1] *= parallelScale; + info.fPhase *= 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); + + 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; + } + + if (info.fIntervals[1] > 0.f) { + GrEffectEdgeType edgeType= useAA ? kFillAA_GrEffectEdgeType : + kFillBW_GrEffectEdgeType; + grPaint.addCoverageEffect( + GrDashingEffect::Create(edgeType, info, coordTrans, strokeWidth))->unref(); + grPaint.setAntiAlias(false); + } + + 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; + } + rect.set(ptsRot, 2); + rect.outset(bloatX + xStroke, bloatY + halfStroke); + context->drawRect(grPaint, rect, NULL, &srcRotInv); + + return true; +} + +////////////////////////////////////////////////////////////////////////////// + +class GLDashingLineEffect; + +class DashingLineEffect : public GrEffect { +public: + typedef SkPathEffect::DashInfo DashInfo; + + /** + * The effect calculates the coverage for the case of a horizontal line in device space. + * The matrix that is passed in should be able to convert a line in source space to a + * horizontal line in device space. Additionally, the coord transform matrix should translate + * the the start of line to origin, and the shift it along the positive x-axis by the phase + * and half the off interval. + */ + static GrEffectRef* Create(GrEffectEdgeType edgeType, const DashInfo& info, + const SkMatrix& matrix, SkScalar strokeWidth); + + virtual ~DashingLineEffect(); + + static const char* Name() { return "DashingEffect"; } + + GrEffectEdgeType getEdgeType() const { return fEdgeType; } + + const SkRect& getRect() const { return fRect; } + + SkScalar getIntervalLength() const { return fIntervalLength; } + + typedef GLDashingLineEffect GLEffect; + + virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE; + + virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE; + +private: + DashingLineEffect(GrEffectEdgeType edgeType, const DashInfo& info, const SkMatrix& matrix, + SkScalar strokeWidth); + + virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE; + + GrEffectEdgeType fEdgeType; + GrCoordTransform fCoordTransform; + SkRect fRect; + SkScalar fIntervalLength; + + GR_DECLARE_EFFECT_TEST; + + typedef GrEffect INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +class GLDashingLineEffect : public GrGLEffect { +public: + GLDashingLineEffect(const GrBackendEffectFactory&, const GrDrawEffect&); + + virtual void emitCode(GrGLShaderBuilder* builder, + const GrDrawEffect& drawEffect, + EffectKey key, + const char* outputColor, + const char* inputColor, + const TransformedCoordsArray&, + const TextureSamplerArray&) SK_OVERRIDE; + + static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&); + + virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE; + +private: + GrGLUniformManager::UniformHandle fRectUniform; + GrGLUniformManager::UniformHandle fIntervalUniform; + SkRect fPrevRect; + SkScalar fPrevIntervalLength; + typedef GrGLEffect INHERITED; +}; + +GLDashingLineEffect::GLDashingLineEffect(const GrBackendEffectFactory& factory, + const GrDrawEffect& drawEffect) + : INHERITED (factory) { + fPrevRect.fLeft = SK_ScalarNaN; + fPrevIntervalLength = SK_ScalarMax; + +} + +void GLDashingLineEffect::emitCode(GrGLShaderBuilder* builder, + const GrDrawEffect& drawEffect, + EffectKey key, + const char* outputColor, + const char* inputColor, + const TransformedCoordsArray& coords, + const TextureSamplerArray& samplers) { + const DashingLineEffect& de = drawEffect.castEffect(); + const char *rectName; + // The rect uniform's xyzw refer to (left + 0.5, top + 0.5, right - 0.5, bottom - 0.5), + // respectively. + fRectUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, + kVec4f_GrSLType, + "rect", + &rectName); + const char *intervalName; + // The interval uniform's refers to the total length of the interval (on + off) + fIntervalUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, + kFloat_GrSLType, + "interval", + &intervalName); + // 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()); + 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. + builder->fsCodeAppend("\t\tfloat xSub, ySub;\n"); + builder->fsCodeAppendf("\t\txSub = min(fragPosShifted.x - %s.x, 0.0);\n", rectName); + builder->fsCodeAppendf("\t\txSub += min(%s.z - fragPosShifted.x, 0.0);\n", rectName); + builder->fsCodeAppendf("\t\tySub = min(fragPosShifted.y - %s.y, 0.0);\n", rectName); + builder->fsCodeAppendf("\t\tySub += min(%s.w - fragPosShifted.y, 0.0);\n", rectName); + // Now compute coverage in x and y and multiply them to get the fraction of the pixel + // covered. + builder->fsCodeAppendf("\t\tfloat alpha = (1.0 + max(xSub, -1.0)) * (1.0 + max(ySub, -1.0));\n"); + } else { + // Assuming the bounding geometry is tight so no need to check y values + builder->fsCodeAppendf("\t\tfloat alpha = 1.0;\n"); + builder->fsCodeAppendf("\t\talpha *= (fragPosShifted.x - %s.x) > -0.5 ? 1.0 : 0.0;\n", rectName); + builder->fsCodeAppendf("\t\talpha *= (%s.z - fragPosShifted.x) >= -0.5 ? 1.0 : 0.0;\n", rectName); + } + builder->fsCodeAppendf("\t\t%s = %s;\n", outputColor, + (GrGLSLExpr4(inputColor) * GrGLSLExpr1("alpha")).c_str()); +} + +void GLDashingLineEffect::setData(const GrGLUniformManager& uman, const GrDrawEffect& drawEffect) { + const DashingLineEffect& de = drawEffect.castEffect(); + const SkRect& rect = de.getRect(); + SkScalar intervalLength = de.getIntervalLength(); + if (rect != fPrevRect || intervalLength != fPrevIntervalLength) { + uman.set4f(fRectUniform, rect.fLeft + 0.5f, rect.fTop + 0.5f, + rect.fRight - 0.5f, rect.fBottom - 0.5f); + uman.set1f(fIntervalUniform, intervalLength); + fPrevRect = rect; + fPrevIntervalLength = intervalLength; + } +} + +GrGLEffect::EffectKey GLDashingLineEffect::GenKey(const GrDrawEffect& drawEffect, + const GrGLCaps&) { + const DashingLineEffect& de = drawEffect.castEffect(); + return de.getEdgeType(); +} + +////////////////////////////////////////////////////////////////////////////// + +GrEffectRef* DashingLineEffect::Create(GrEffectEdgeType edgeType, const DashInfo& info, + const SkMatrix& matrix, SkScalar strokeWidth) { + if (info.fCount != 2) { + return NULL; + } + + return CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(DashingLineEffect, + (edgeType, info, matrix, strokeWidth)))); +} + +DashingLineEffect::~DashingLineEffect() {} + +void DashingLineEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const { + *validFlags = 0; +} + +const GrBackendEffectFactory& DashingLineEffect::getFactory() const { + return GrTBackendEffectFactory::getInstance(); +} + +DashingLineEffect::DashingLineEffect(GrEffectEdgeType edgeType, const DashInfo& info, + const SkMatrix& matrix, SkScalar strokeWidth) + : fEdgeType(edgeType) + , fCoordTransform(kLocal_GrCoordSet, matrix) { + SkScalar onLen = info.fIntervals[0]; + SkScalar offLen = info.fIntervals[1]; + SkScalar halfOffLen = SkScalarHalf(offLen); + SkScalar halfStroke = SkScalarHalf(strokeWidth); + fIntervalLength = onLen + offLen; + fRect.set(halfOffLen, -halfStroke, halfOffLen + onLen, halfStroke); + + addCoordTransform(&fCoordTransform); +} + +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); +} + +GR_DEFINE_EFFECT_TEST(DashingLineEffect); + +GrEffectRef* DashingLineEffect::TestCreate(SkRandom* random, + GrContext*, + const GrDrawTargetCaps& caps, + GrTexture*[]) { + GrEffectRef* effect; + SkMatrix m; + m.reset(); + GrEffectEdgeType edgeType = static_cast(random->nextULessThan( + kGrEffectEdgeTypeCnt)); + SkScalar strokeWidth = random->nextRangeScalar(0, 100.f); + DashInfo info; + info.fCount = 2; + SkAutoTArray intervals(info.fCount); + info.fIntervals = intervals.get(); + info.fIntervals[0] = random->nextRangeScalar(0, 10.f); + 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); + return effect; +} + +////////////////////////////////////////////////////////////////////////////// + +GrEffectRef* GrDashingEffect::Create(GrEffectEdgeType edgeType, const SkPathEffect::DashInfo& info, + const SkMatrix& matrix, SkScalar strokeWidth) { + return DashingLineEffect::Create(edgeType, info, matrix, strokeWidth); +} diff --git a/src/gpu/effects/GrDashingEffect.h b/src/gpu/effects/GrDashingEffect.h new file mode 100644 index 0000000..a1c2829 --- /dev/null +++ b/src/gpu/effects/GrDashingEffect.h @@ -0,0 +1,33 @@ + +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrDashingEffect_DEFINED +#define GrDashingEffect_DEFINED + +#include "GrTypesPriv.h" +#include "SkPathEffect.h" + +class SkGpuDevice; + +class GrGLDashingEffect; +class SkPath; + +namespace GrDashingEffect { + bool DrawDashLine(const SkPoint pnts[2], const SkPaint& paint, SkGpuDevice* dev); + + /** + * An effect that renders a dashed line. It is intended to be used as a coverage effect. + * The effect is meant for dashed lines that only have a single on/off interval pair. + * Bounding geometry is rendered and the effect computes coverage based on the fragment's + * position relative to the dashed line. + */ + GrEffectRef* Create(GrEffectEdgeType edgeType, const SkPathEffect::DashInfo& info, + const SkMatrix& matrix, SkScalar strokeWidth); +} + +#endif