GrDashingEffect.cpp revision 9853ccef19c200be93a6211f32589fa82a53067c
18d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi/*
28d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi * Copyright 2014 Google Inc.
38d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi *
48d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi * Use of this source code is governed by a BSD-style license that can be
58d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi * found in the LICENSE file.
68d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi */
78d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
88d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi#include "GrDashingEffect.h"
98d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
108d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi#include "../GrAARectRenderer.h"
118d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
128d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi#include "GrGeometryProcessor.h"
138d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi#include "GrContext.h"
148d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi#include "GrCoordTransform.h"
158d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi#include "GrDefaultGeoProcFactory.h"
168d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi#include "GrDrawTarget.h"
178d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi#include "GrDrawTargetCaps.h"
188d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi#include "GrInvariantOutput.h"
198d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi#include "GrProcessor.h"
208d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi#include "GrStrokeInfo.h"
218d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi#include "GrTBackendProcessorFactory.h"
228d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi#include "SkGr.h"
238d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi#include "gl/GrGLGeometryProcessor.h"
248d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi#include "gl/GrGLProcessor.h"
258d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi#include "gl/GrGLSL.h"
268d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi#include "gl/builders/GrGLProgramBuilder.h"
278d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
288d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi///////////////////////////////////////////////////////////////////////////////
298d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
308d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi// Returns whether or not the gpu can fast path the dash line effect.
318d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoistatic bool can_fast_path_dash(const SkPoint pts[2], const GrStrokeInfo& strokeInfo,
328d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                               const GrDrawTarget& target, const GrDrawState& ds,
338d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                               const SkMatrix& viewMatrix) {
348d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if (ds.getRenderTarget()->isMultisampled()) {
358d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        return false;
368d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    }
378d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
388d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    // Pts must be either horizontal or vertical in src space
398d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if (pts[0].fX != pts[1].fX && pts[0].fY != pts[1].fY) {
408d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        return false;
418d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    }
428d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
438d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    // May be able to relax this to include skew. As of now cannot do perspective
448d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    // because of the non uniform scaling of bloating a rect
458d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if (!viewMatrix.preservesRightAngles()) {
468d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        return false;
478d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    }
488d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
498d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if (!strokeInfo.isDashed() || 2 != strokeInfo.dashCount()) {
508d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        return false;
518d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    }
528d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
538d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    const SkPathEffect::DashInfo& info = strokeInfo.getDashInfo();
548d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if (0 == info.fIntervals[0] && 0 == info.fIntervals[1]) {
558d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        return false;
568d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    }
578d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
588d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    SkPaint::Cap cap = strokeInfo.getStrokeRec().getCap();
598d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    // Current we do don't handle Round or Square cap dashes
608d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if (SkPaint::kRound_Cap == cap && info.fIntervals[0] != 0.f) {
618d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        return false;
628d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    }
638d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
648d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    return true;
658d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi}
668d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
678d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoinamespace {
688d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
698d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoistruct DashLineVertex {
708d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    SkPoint fPos;
718d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    SkPoint fDashPos;
728d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi};
738d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
748d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoiextern const GrVertexAttrib gDashLineVertexAttribs[] = {
758d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    { kVec2f_GrVertexAttribType, 0,                 kPosition_GrVertexAttribBinding },
768d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    { kVec2f_GrVertexAttribType, sizeof(SkPoint),   kGeometryProcessor_GrVertexAttribBinding },
778d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi};
788d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
798d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi};
808d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoistatic void calc_dash_scaling(SkScalar* parallelScale, SkScalar* perpScale,
818d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                            const SkMatrix& viewMatrix, const SkPoint pts[2]) {
828d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    SkVector vecSrc = pts[1] - pts[0];
838d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    SkScalar magSrc = vecSrc.length();
848d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    SkScalar invSrc = magSrc ? SkScalarInvert(magSrc) : 0;
858d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    vecSrc.scale(invSrc);
868d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
878d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    SkVector vecSrcPerp;
888d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    vecSrc.rotateCW(&vecSrcPerp);
898d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    viewMatrix.mapVectors(&vecSrc, 1);
908d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    viewMatrix.mapVectors(&vecSrcPerp, 1);
918d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
928d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    // parallelScale tells how much to scale along the line parallel to the dash line
938d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    // perpScale tells how much to scale in the direction perpendicular to the dash line
948d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    *parallelScale = vecSrc.length();
958d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    *perpScale = vecSrcPerp.length();
968d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi}
978d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
988d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi// calculates the rotation needed to aligned pts to the x axis with pts[0] < pts[1]
99cef7893435aa41160dd1255c43cb8498279738ccChris Craik// Stores the rotation matrix in rotMatrix, and the mapped points in ptsRot
100cef7893435aa41160dd1255c43cb8498279738ccChris Craikstatic void align_to_x_axis(const SkPoint pts[2], SkMatrix* rotMatrix, SkPoint ptsRot[2] = NULL) {
101cef7893435aa41160dd1255c43cb8498279738ccChris Craik    SkVector vec = pts[1] - pts[0];
1028d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    SkScalar mag = vec.length();
1038d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    SkScalar inv = mag ? SkScalarInvert(mag) : 0;
1048d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1058d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    vec.scale(inv);
1068d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    rotMatrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
1078d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if (ptsRot) {
1088d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        rotMatrix->mapPoints(ptsRot, pts, 2);
1098d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        // correction for numerical issues if map doesn't make ptsRot exactly horizontal
1108d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        ptsRot[1].fY = pts[0].fY;
1118d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    }
1128d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi}
1138d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1148d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi// Assumes phase < sum of all intervals
1158d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoistatic SkScalar calc_start_adjustment(const SkPathEffect::DashInfo& info) {
1168d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    SkASSERT(info.fPhase < info.fIntervals[0] + info.fIntervals[1]);
1178d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if (info.fPhase >= info.fIntervals[0] && info.fPhase != 0) {
1188d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        SkScalar srcIntervalLen = info.fIntervals[0] + info.fIntervals[1];
1198d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        return srcIntervalLen - info.fPhase;
1208d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    }
1218d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    return 0;
1228d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi}
1238d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1248d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoistatic SkScalar calc_end_adjustment(const SkPathEffect::DashInfo& info, const SkPoint pts[2],
1258d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                                    SkScalar phase, SkScalar* endingInt) {
1268d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if (pts[1].fX <= pts[0].fX) {
1278d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        return 0;
1288d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    }
1298d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    SkScalar srcIntervalLen = info.fIntervals[0] + info.fIntervals[1];
1308d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    SkScalar totalLen = pts[1].fX - pts[0].fX;
1318d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    SkScalar temp = SkScalarDiv(totalLen, srcIntervalLen);
1328d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    SkScalar numFullIntervals = SkScalarFloorToScalar(temp);
1338d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    *endingInt = totalLen - numFullIntervals * srcIntervalLen + phase;
1348d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    temp = SkScalarDiv(*endingInt, srcIntervalLen);
1358d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    *endingInt = *endingInt - SkScalarFloorToScalar(temp) * srcIntervalLen;
1368d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if (0 == *endingInt) {
1378d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        *endingInt = srcIntervalLen;
1388d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    }
1398d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if (*endingInt > info.fIntervals[0]) {
1408d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        if (0 == info.fIntervals[0]) {
1418d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi            *endingInt -= 0.01f; // make sure we capture the last zero size pnt (used if has caps)
1428d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        }
1438d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        return *endingInt - info.fIntervals[0];
1448d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    }
1458d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    return 0;
1468d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi}
1478d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1488d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoistatic void setup_dashed_rect(const SkRect& rect, DashLineVertex* verts, int idx, const SkMatrix& matrix,
1498d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                       SkScalar offset, SkScalar bloat, SkScalar len, SkScalar stroke) {
1508d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        SkScalar startDashX = offset - bloat;
1518d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        SkScalar endDashX = offset + len + bloat;
1528d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        SkScalar startDashY = -stroke - bloat;
1538d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        SkScalar endDashY = stroke + bloat;
1548d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        verts[idx].fDashPos = SkPoint::Make(startDashX , startDashY);
1558d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        verts[idx + 1].fDashPos = SkPoint::Make(startDashX, endDashY);
1568d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        verts[idx + 2].fDashPos = SkPoint::Make(endDashX, endDashY);
1578d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        verts[idx + 3].fDashPos = SkPoint::Make(endDashX, startDashY);
1588d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        verts[idx].fPos = SkPoint::Make(rect.fLeft, rect.fTop);
1598d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        verts[idx + 1].fPos = SkPoint::Make(rect.fLeft, rect.fBottom);
1608d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        verts[idx + 2].fPos = SkPoint::Make(rect.fRight, rect.fBottom);
1618d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        verts[idx + 3].fPos = SkPoint::Make(rect.fRight, rect.fTop);
1628d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        matrix.mapPointsWithStride(&verts[idx].fPos, sizeof(DashLineVertex), 4);
1638d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi}
1648d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1658d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoistatic void setup_dashed_rect_pos(const SkRect& rect, int idx, const SkMatrix& matrix,
1668d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                                  SkPoint* verts) {
1678d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    verts[idx] = SkPoint::Make(rect.fLeft, rect.fTop);
1688d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    verts[idx + 1] = SkPoint::Make(rect.fLeft, rect.fBottom);
1698d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    verts[idx + 2] = SkPoint::Make(rect.fRight, rect.fBottom);
1708d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    verts[idx + 3] = SkPoint::Make(rect.fRight, rect.fTop);
1718d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    matrix.mapPoints(&verts[idx], 4);
1728d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi}
1738d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1748d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoibool GrDashingEffect::DrawDashLine(GrGpu* gpu, GrDrawTarget* target, GrDrawState* drawState,
1758d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                                   const SkPoint pts[2], const GrPaint& paint,
1768d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                                   const GrStrokeInfo& strokeInfo, const SkMatrix& vm) {
1778d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1788d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if (!can_fast_path_dash(pts, strokeInfo, *target, *drawState, vm)) {
1798d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        return false;
1808d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    }
1818d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1828d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    const SkPathEffect::DashInfo& info = strokeInfo.getDashInfo();
1838d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1848d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    SkPaint::Cap cap = strokeInfo.getStrokeRec().getCap();
1858d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1868d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    SkScalar srcStrokeWidth = strokeInfo.getStrokeRec().getWidth();
1878d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1888d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    // the phase should be normalized to be [0, sum of all intervals)
1898d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    SkASSERT(info.fPhase >= 0 && info.fPhase < info.fIntervals[0] + info.fIntervals[1]);
1908d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1918d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    SkScalar srcPhase = info.fPhase;
1928d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1938d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    // Rotate the src pts so they are aligned horizontally with pts[0].fX < pts[1].fX
1948d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    SkMatrix srcRotInv;
1958d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    SkPoint ptsRot[2];
1968d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if (pts[0].fY != pts[1].fY || pts[0].fX > pts[1].fX) {
1978d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        SkMatrix rotMatrix;
1988d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        align_to_x_axis(pts, &rotMatrix, ptsRot);
1998d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        if(!rotMatrix.invert(&srcRotInv)) {
2008d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi            SkDebugf("Failed to create invertible rotation matrix!\n");
2018d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi            return false;
2028d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        }
2038d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    } else {
2048d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        srcRotInv.reset();
2058d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        memcpy(ptsRot, pts, 2 * sizeof(SkPoint));
2068d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    }
2078d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
2088d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    bool useAA = paint.isAntiAlias();
2098d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
2108d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    // Scale corrections of intervals and stroke from view matrix
2118d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    SkScalar parallelScale;
2128d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    SkScalar perpScale;
2138d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    calc_dash_scaling(&parallelScale, &perpScale, vm, ptsRot);
2148d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
2158d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    bool hasCap = SkPaint::kButt_Cap != cap && 0 != srcStrokeWidth;
2168d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
2178d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    // We always want to at least stroke out half a pixel on each side in device space
2188d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    // so 0.5f / perpScale gives us this min in src space
2198d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    SkScalar halfSrcStroke = SkMaxScalar(srcStrokeWidth * 0.5f, 0.5f / perpScale);
2208d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
2218d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    SkScalar strokeAdj;
2228d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if (!hasCap) {
2238d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        strokeAdj = 0.f;
2248d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    } else {
2258d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        strokeAdj = halfSrcStroke;
2268d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    }
2278d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
2288d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    SkScalar startAdj = 0;
2298d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
2308d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    SkMatrix combinedMatrix = srcRotInv;
2318d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    combinedMatrix.postConcat(vm);
2328d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
2338d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    bool lineDone = false;
2348d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    SkRect startRect;
2358d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    bool hasStartRect = false;
2368d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    // If we are using AA, check to see if we are drawing a partial dash at the start. If so
2378d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    // draw it separately here and adjust our start point accordingly
2388d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if (useAA) {
2398d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        if (srcPhase > 0 && srcPhase < info.fIntervals[0]) {
2408d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi            SkPoint startPts[2];
2418d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi            startPts[0] = ptsRot[0];
2428d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi            startPts[1].fY = startPts[0].fY;
2438d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi            startPts[1].fX = SkMinScalar(startPts[0].fX + info.fIntervals[0] - srcPhase,
2448d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                                         ptsRot[1].fX);
2458d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi            startRect.set(startPts, 2);
2468d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi            startRect.outset(strokeAdj, halfSrcStroke);
2478d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
2488d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi            hasStartRect = true;
2498d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi            startAdj = info.fIntervals[0] + info.fIntervals[1] - srcPhase;
2508d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        }
2518d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    }
2528d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
2538d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    // adjustments for start and end of bounding rect so we only draw dash intervals
2548d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    // contained in the original line segment.
2558d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    startAdj += calc_start_adjustment(info);
2568d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if (startAdj != 0) {
2578d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        ptsRot[0].fX += startAdj;
2588d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        srcPhase = 0;
2598d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    }
2608d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    SkScalar endingInterval = 0;
2618d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    SkScalar endAdj = calc_end_adjustment(info, ptsRot, srcPhase, &endingInterval);
2628d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    ptsRot[1].fX -= endAdj;
2638d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if (ptsRot[0].fX >= ptsRot[1].fX) {
2648d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        lineDone = true;
2658d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    }
2668d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
2678d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    SkRect endRect;
2688d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    bool hasEndRect = false;
2698d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    // If we are using AA, check to see if we are drawing a partial dash at then end. If so
2708d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    // draw it separately here and adjust our end point accordingly
2718d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if (useAA && !lineDone) {
2728d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        // If we adjusted the end then we will not be drawing a partial dash at the end.
2738d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        // If we didn't adjust the end point then we just need to make sure the ending
2748d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        // dash isn't a full dash
2758d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        if (0 == endAdj && endingInterval != info.fIntervals[0]) {
2768d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi            SkPoint endPts[2];
2778d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi            endPts[1] = ptsRot[1];
2788d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi            endPts[0].fY = endPts[1].fY;
2798d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi            endPts[0].fX = endPts[1].fX - endingInterval;
2808d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
2818d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi            endRect.set(endPts, 2);
2828d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi            endRect.outset(strokeAdj, halfSrcStroke);
2838d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
2848d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi            hasEndRect = true;
2858d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi            endAdj = endingInterval + info.fIntervals[1];
2868d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
2878d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi            ptsRot[1].fX -= endAdj;
2888d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi            if (ptsRot[0].fX >= ptsRot[1].fX) {
2898d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                lineDone = true;
2908d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi            }
2918d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        }
2928d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    }
2938d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
2948d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if (startAdj != 0) {
2958d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        srcPhase = 0;
2968d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    }
2978d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
2988d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    // Change the dashing info from src space into device space
2998d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    SkScalar devIntervals[2];
3008d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    devIntervals[0] = info.fIntervals[0] * parallelScale;
3018d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    devIntervals[1] = info.fIntervals[1] * parallelScale;
3028d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    SkScalar devPhase = srcPhase * parallelScale;
3038d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    SkScalar strokeWidth = srcStrokeWidth * perpScale;
3048d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3058d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if ((strokeWidth < 1.f && !useAA) || 0.f == strokeWidth) {
3068d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        strokeWidth = 1.f;
3078d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    }
3088d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3098d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    SkScalar halfDevStroke = strokeWidth * 0.5f;
3108d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3118d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if (SkPaint::kSquare_Cap == cap && 0 != srcStrokeWidth) {
3128d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        // add cap to on interveal and remove from off interval
3138d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        devIntervals[0] += strokeWidth;
3148d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        devIntervals[1] -= strokeWidth;
3158d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    }
3168d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    SkScalar startOffset = devIntervals[1] * 0.5f + devPhase;
3178d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3188d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    SkScalar bloatX = useAA ? 0.5f / parallelScale : 0.f;
3198d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    SkScalar bloatY = useAA ? 0.5f / perpScale : 0.f;
3208d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3218d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    SkScalar devBloat = useAA ? 0.5f : 0.f;
3228d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3238d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if (devIntervals[1] <= 0.f && useAA) {
3248d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        // Case when we end up drawing a solid AA rect
3258d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        // Reset the start rect to draw this single solid rect
3268d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        // but it requires to upload a new intervals uniform so we can mimic
3278d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        // one giant dash
3288d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        ptsRot[0].fX -= hasStartRect ? startAdj : 0;
3298d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        ptsRot[1].fX += hasEndRect ? endAdj : 0;
3308d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        startRect.set(ptsRot, 2);
3318d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        startRect.outset(strokeAdj, halfSrcStroke);
3328d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        hasStartRect = true;
3338d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        hasEndRect = false;
3348d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        lineDone = true;
3358d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3368d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        SkPoint devicePts[2];
3378d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        vm.mapPoints(devicePts, ptsRot, 2);
3388d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        SkScalar lineLength = SkPoint::Distance(devicePts[0], devicePts[1]);
3398d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        if (hasCap) {
3408d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi            lineLength += 2.f * halfDevStroke;
3418d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        }
3428d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        devIntervals[0] = lineLength;
3438d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    }
3448d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    bool fullDash = devIntervals[1] > 0.f || useAA;
3458d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if (fullDash) {
3468d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        SkPathEffect::DashInfo devInfo;
3478d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        devInfo.fPhase = devPhase;
3488d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        devInfo.fCount = 2;
3498d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        devInfo.fIntervals = devIntervals;
3508d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        GrPrimitiveEdgeType edgeType= useAA ? kFillAA_GrProcessorEdgeType :
3518d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi            kFillBW_GrProcessorEdgeType;
3528d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        bool isRoundCap = SkPaint::kRound_Cap == cap;
3538d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        GrDashingEffect::DashCap capType = isRoundCap ? GrDashingEffect::kRound_DashCap :
3548d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                                                        GrDashingEffect::kNonRound_DashCap;
3558d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        drawState->setGeometryProcessor(
3568d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                GrDashingEffect::Create(edgeType, devInfo, strokeWidth, capType))->unref();
3578d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3588d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        // Set up the vertex data for the line and start/end dashes
3598d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        drawState->setVertexAttribs<gDashLineVertexAttribs>(SK_ARRAY_COUNT(gDashLineVertexAttribs),
3608d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                                                            sizeof(DashLineVertex));
3618d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    } else {
3628d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        // Set up the vertex data for the line and start/end dashes
3638d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        drawState->setGeometryProcessor(
3648d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                GrDefaultGeoProcFactory::CreateAndSetAttribs(
3658d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                        drawState,
3668d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                        GrDefaultGeoProcFactory::kPosition_GPType))->unref();
3678d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    }
3688d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3698d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    int totalRectCnt = 0;
3708d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3718d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    totalRectCnt += !lineDone ? 1 : 0;
3728d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    totalRectCnt += hasStartRect ? 1 : 0;
3738d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    totalRectCnt += hasEndRect ? 1 : 0;
3748d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3758d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    GrDrawTarget::AutoReleaseGeometry geo(target,
3768d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                                          totalRectCnt * 4,
3778d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                                          drawState->getVertexStride(), 0);
3788d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if (!geo.succeeded()) {
3798d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        SkDebugf("Failed to get space for vertices!\n");
3808d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        return false;
3818d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    }
3828d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3838d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    int curVIdx = 0;
3848d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3858d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if (SkPaint::kRound_Cap == cap && 0 != srcStrokeWidth) {
3868d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        // need to adjust this for round caps to correctly set the dashPos attrib on vertices
3878d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        startOffset -= halfDevStroke;
3888d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    }
3898d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3908d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    // Draw interior part of dashed line
3918d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if (!lineDone) {
3928d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        SkPoint devicePts[2];
3938d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        vm.mapPoints(devicePts, ptsRot, 2);
3948d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        SkScalar lineLength = SkPoint::Distance(devicePts[0], devicePts[1]);
3958d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        if (hasCap) {
3968d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi            lineLength += 2.f * halfDevStroke;
3978d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        }
3988d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3998d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        SkRect bounds;
4008d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        bounds.set(ptsRot[0].fX, ptsRot[0].fY, ptsRot[1].fX, ptsRot[1].fY);
4018d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        bounds.outset(bloatX + strokeAdj, bloatY + halfSrcStroke);
4028d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        if (fullDash) {
4038d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi            DashLineVertex* verts = reinterpret_cast<DashLineVertex*>(geo.vertices());
4048d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi            setup_dashed_rect(bounds, verts, curVIdx, combinedMatrix, startOffset, devBloat,
4058d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                                      lineLength, halfDevStroke);
4068d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        } else {
4078d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi            SkPoint* verts = reinterpret_cast<SkPoint*>(geo.vertices());
4088d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi            setup_dashed_rect_pos(bounds, curVIdx, combinedMatrix, verts);
4098d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        }
4108d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        curVIdx += 4;
4118d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    }
4128d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
4138d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if (hasStartRect) {
4148d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        SkASSERT(useAA);  // so that we know bloatX and bloatY have been set
4158d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        startRect.outset(bloatX, bloatY);
4168d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        if (fullDash) {
4178d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi            DashLineVertex* verts = reinterpret_cast<DashLineVertex*>(geo.vertices());
4188d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi            setup_dashed_rect(startRect, verts, curVIdx, combinedMatrix, startOffset, devBloat,
4198d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                              devIntervals[0], halfDevStroke);
4208d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        } else {
4218d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi            SkPoint* verts = reinterpret_cast<SkPoint*>(geo.vertices());
4228d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi            setup_dashed_rect_pos(startRect, curVIdx, combinedMatrix, verts);
4238d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        }
4248d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
4258d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        curVIdx += 4;
4268d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    }
4278d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
4288d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if (hasEndRect) {
4298d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        SkASSERT(useAA);  // so that we know bloatX and bloatY have been set
4308d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        endRect.outset(bloatX, bloatY);
4318d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        if (fullDash) {
4328d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi            DashLineVertex* verts = reinterpret_cast<DashLineVertex*>(geo.vertices());
4338d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi            setup_dashed_rect(endRect, verts, curVIdx, combinedMatrix, startOffset, devBloat,
4348d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                              devIntervals[0], halfDevStroke);
4358d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        } else {
4368d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi            SkPoint* verts = reinterpret_cast<SkPoint*>(geo.vertices());
4378d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi            setup_dashed_rect_pos(endRect, curVIdx, combinedMatrix, verts);
4388d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        }
4398d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
4408d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    }
4418d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
4428d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    target->setIndexSourceToBuffer(gpu->getContext()->getQuadIndexBuffer());
4438d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    target->drawIndexedInstances(drawState, kTriangles_GrPrimitiveType, totalRectCnt, 4, 6);
4448d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    target->resetIndexSource();
4458d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    return true;
4468d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi}
4478d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
4488d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi//////////////////////////////////////////////////////////////////////////////
4498d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
4508d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoiclass GLDashingCircleEffect;
4518d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi/*
4528d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi * This effect will draw a dotted line (defined as a dashed lined with round caps and no on
4538d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi * interval). The radius of the dots is given by the strokeWidth and the spacing by the DashInfo.
4548d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi * Both of the previous two parameters are in device space. This effect also requires the setting of
4558d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi * a vec2 vertex attribute for the the four corners of the bounding rect. This attribute is the
4568d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi * "dash position" of each vertex. In other words it is the vertex coords (in device space) if we
4578d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi * transform the line to be horizontal, with the start of line at the origin then shifted to the
4588d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi * right by half the off interval. The line then goes in the positive x direction.
4598d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi */
4608d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoiclass DashingCircleEffect : public GrGeometryProcessor {
4618d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoipublic:
4628d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    typedef SkPathEffect::DashInfo DashInfo;
4638d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
4648d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    static GrGeometryProcessor* Create(GrPrimitiveEdgeType edgeType,
4658d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                                       const DashInfo& info,
4668d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                                       SkScalar radius);
4678d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
4688d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    virtual ~DashingCircleEffect();
4698d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
4708d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    static const char* Name() { return "DashingCircleEffect"; }
4718d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
4728d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    const GrShaderVar& inCoord() const { return fInCoord; }
4738d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
4748d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    GrPrimitiveEdgeType getEdgeType() const { return fEdgeType; }
475
476    SkScalar getRadius() const { return fRadius; }
477
478    SkScalar getCenterX() const { return fCenterX; }
479
480    SkScalar getIntervalLength() const { return fIntervalLength; }
481
482    typedef GLDashingCircleEffect GLProcessor;
483
484    virtual const GrBackendGeometryProcessorFactory& getFactory() const SK_OVERRIDE;
485
486private:
487    DashingCircleEffect(GrPrimitiveEdgeType edgeType, const DashInfo& info, SkScalar radius);
488
489    virtual bool onIsEqual(const GrGeometryProcessor& other) const SK_OVERRIDE;
490
491    virtual void onComputeInvariantOutput(GrInvariantOutput* inout) const SK_OVERRIDE;
492
493    GrPrimitiveEdgeType    fEdgeType;
494    const GrShaderVar&  fInCoord;
495    SkScalar            fIntervalLength;
496    SkScalar            fRadius;
497    SkScalar            fCenterX;
498
499    GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
500
501    typedef GrGeometryProcessor INHERITED;
502};
503
504//////////////////////////////////////////////////////////////////////////////
505
506class GLDashingCircleEffect : public GrGLGeometryProcessor {
507public:
508    GLDashingCircleEffect(const GrBackendProcessorFactory&, const GrProcessor&);
509
510    virtual void emitCode(const EmitArgs&) SK_OVERRIDE;
511
512    static inline void GenKey(const GrProcessor&, const GrGLCaps&, GrProcessorKeyBuilder*);
513
514    virtual void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
515
516private:
517    GrGLProgramDataManager::UniformHandle fParamUniform;
518    SkScalar                              fPrevRadius;
519    SkScalar                              fPrevCenterX;
520    SkScalar                              fPrevIntervalLength;
521    typedef GrGLGeometryProcessor INHERITED;
522};
523
524GLDashingCircleEffect::GLDashingCircleEffect(const GrBackendProcessorFactory& factory,
525                                             const GrProcessor&)
526    : INHERITED (factory) {
527    fPrevRadius = SK_ScalarMin;
528    fPrevCenterX = SK_ScalarMin;
529    fPrevIntervalLength = SK_ScalarMax;
530}
531
532void GLDashingCircleEffect::emitCode(const EmitArgs& args) {
533    const DashingCircleEffect& dce = args.fGP.cast<DashingCircleEffect>();
534    const char *paramName;
535    // The param uniforms, xyz, refer to circle radius - 0.5, cicles center x coord, and
536    // the total interval length of the dash.
537    fParamUniform = args.fPB->addUniform(GrGLProgramBuilder::kFragment_Visibility,
538                                         kVec3f_GrSLType,
539                                         "params",
540                                         &paramName);
541
542    GrGLVertToFrag v(kVec2f_GrSLType);
543    args.fPB->addVarying("Coord", &v);
544
545    GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
546    vsBuilder->codeAppendf("\t%s = %s;\n", v.vsOut(), dce.inCoord().c_str());
547
548    // setup position varying
549    vsBuilder->codeAppendf("%s = %s * vec3(%s, 1);", vsBuilder->glPosition(), vsBuilder->uViewM(),
550                           vsBuilder->inPosition());
551
552    // transforms all points so that we can compare them to our test circle
553    GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
554    fsBuilder->codeAppendf("\t\tfloat xShifted = %s.x - floor(%s.x / %s.z) * %s.z;\n",
555                           v.fsIn(), v.fsIn(), paramName, paramName);
556    fsBuilder->codeAppendf("\t\tvec2 fragPosShifted = vec2(xShifted, %s.y);\n", v.fsIn());
557    fsBuilder->codeAppendf("\t\tvec2 center = vec2(%s.y, 0.0);\n", paramName);
558    fsBuilder->codeAppend("\t\tfloat dist = length(center - fragPosShifted);\n");
559    if (GrProcessorEdgeTypeIsAA(dce.getEdgeType())) {
560        fsBuilder->codeAppendf("\t\tfloat diff = dist - %s.x;\n", paramName);
561        fsBuilder->codeAppend("\t\tdiff = 1.0 - diff;\n");
562        fsBuilder->codeAppend("\t\tfloat alpha = clamp(diff, 0.0, 1.0);\n");
563    } else {
564        fsBuilder->codeAppendf("\t\tfloat alpha = 1.0;\n");
565        fsBuilder->codeAppendf("\t\talpha *=  dist < %s.x + 0.5 ? 1.0 : 0.0;\n", paramName);
566    }
567    fsBuilder->codeAppendf("\t\t%s = %s;\n", args.fOutput,
568                           (GrGLSLExpr4(args.fInput) * GrGLSLExpr1("alpha")).c_str());
569}
570
571void GLDashingCircleEffect::setData(const GrGLProgramDataManager& pdman
572                                    , const GrProcessor& processor) {
573    const DashingCircleEffect& dce = processor.cast<DashingCircleEffect>();
574    SkScalar radius = dce.getRadius();
575    SkScalar centerX = dce.getCenterX();
576    SkScalar intervalLength = dce.getIntervalLength();
577    if (radius != fPrevRadius || centerX != fPrevCenterX || intervalLength != fPrevIntervalLength) {
578        pdman.set3f(fParamUniform, radius - 0.5f, centerX, intervalLength);
579        fPrevRadius = radius;
580        fPrevCenterX = centerX;
581        fPrevIntervalLength = intervalLength;
582    }
583}
584
585void GLDashingCircleEffect::GenKey(const GrProcessor& processor, const GrGLCaps&,
586                                   GrProcessorKeyBuilder* b) {
587    const DashingCircleEffect& dce = processor.cast<DashingCircleEffect>();
588    b->add32(dce.getEdgeType());
589}
590
591//////////////////////////////////////////////////////////////////////////////
592
593GrGeometryProcessor* DashingCircleEffect::Create(GrPrimitiveEdgeType edgeType, const DashInfo& info,
594                                                 SkScalar radius) {
595    if (info.fCount != 2 || info.fIntervals[0] != 0) {
596        return NULL;
597    }
598
599    return SkNEW_ARGS(DashingCircleEffect, (edgeType, info, radius));
600}
601
602DashingCircleEffect::~DashingCircleEffect() {}
603
604void DashingCircleEffect::onComputeInvariantOutput(GrInvariantOutput* inout) const {
605    inout->mulByUnknownAlpha();
606}
607
608const GrBackendGeometryProcessorFactory& DashingCircleEffect::getFactory() const {
609    return GrTBackendGeometryProcessorFactory<DashingCircleEffect>::getInstance();
610}
611
612DashingCircleEffect::DashingCircleEffect(GrPrimitiveEdgeType edgeType, const DashInfo& info,
613                                         SkScalar radius)
614    : fEdgeType(edgeType)
615    , fInCoord(this->addVertexAttrib(GrShaderVar("inCoord",
616                                                 kVec2f_GrSLType,
617                                                 GrShaderVar::kAttribute_TypeModifier))) {
618    SkScalar onLen = info.fIntervals[0];
619    SkScalar offLen = info.fIntervals[1];
620    fIntervalLength = onLen + offLen;
621    fRadius = radius;
622    fCenterX = SkScalarHalf(offLen);
623}
624
625bool DashingCircleEffect::onIsEqual(const GrGeometryProcessor& other) const {
626    const DashingCircleEffect& dce = other.cast<DashingCircleEffect>();
627    return (fEdgeType == dce.fEdgeType &&
628            fIntervalLength == dce.fIntervalLength &&
629            fRadius == dce.fRadius &&
630            fCenterX == dce.fCenterX);
631}
632
633GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DashingCircleEffect);
634
635GrGeometryProcessor* DashingCircleEffect::TestCreate(SkRandom* random,
636                                                     GrContext*,
637                                                     const GrDrawTargetCaps& caps,
638                                                     GrTexture*[]) {
639    GrPrimitiveEdgeType edgeType = static_cast<GrPrimitiveEdgeType>(random->nextULessThan(
640            kGrProcessorEdgeTypeCnt));
641    SkScalar strokeWidth = random->nextRangeScalar(0, 100.f);
642    DashInfo info;
643    info.fCount = 2;
644    SkAutoTArray<SkScalar> intervals(info.fCount);
645    info.fIntervals = intervals.get();
646    info.fIntervals[0] = 0;
647    info.fIntervals[1] = random->nextRangeScalar(0, 10.f);
648    info.fPhase = random->nextRangeScalar(0, info.fIntervals[1]);
649
650    return DashingCircleEffect::Create(edgeType, info, strokeWidth);
651}
652
653//////////////////////////////////////////////////////////////////////////////
654
655class GLDashingLineEffect;
656
657/*
658 * This effect will draw a dashed line. The width of the dash is given by the strokeWidth and the
659 * length and spacing by the DashInfo. Both of the previous two parameters are in device space.
660 * This effect also requires the setting of a vec2 vertex attribute for the the four corners of the
661 * bounding rect. This attribute is the "dash position" of each vertex. In other words it is the
662 * vertex coords (in device space) if we transform the line to be horizontal, with the start of
663 * line at the origin then shifted to the right by half the off interval. The line then goes in the
664 * positive x direction.
665 */
666class DashingLineEffect : public GrGeometryProcessor {
667public:
668    typedef SkPathEffect::DashInfo DashInfo;
669
670    static GrGeometryProcessor* Create(GrPrimitiveEdgeType edgeType,
671                                       const DashInfo& info,
672                                       SkScalar strokeWidth);
673
674    virtual ~DashingLineEffect();
675
676    static const char* Name() { return "DashingEffect"; }
677
678    const GrShaderVar& inCoord() const { return fInCoord; }
679
680    GrPrimitiveEdgeType getEdgeType() const { return fEdgeType; }
681
682    const SkRect& getRect() const { return fRect; }
683
684    SkScalar getIntervalLength() const { return fIntervalLength; }
685
686    typedef GLDashingLineEffect GLProcessor;
687
688    virtual const GrBackendGeometryProcessorFactory& getFactory() const SK_OVERRIDE;
689
690private:
691    DashingLineEffect(GrPrimitiveEdgeType edgeType, const DashInfo& info, SkScalar strokeWidth);
692
693    virtual bool onIsEqual(const GrGeometryProcessor& other) const SK_OVERRIDE;
694
695    virtual void onComputeInvariantOutput(GrInvariantOutput* inout) const SK_OVERRIDE;
696
697    GrPrimitiveEdgeType    fEdgeType;
698    const GrShaderVar&  fInCoord;
699    SkRect              fRect;
700    SkScalar            fIntervalLength;
701
702    GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
703
704    typedef GrGeometryProcessor INHERITED;
705};
706
707//////////////////////////////////////////////////////////////////////////////
708
709class GLDashingLineEffect : public GrGLGeometryProcessor {
710public:
711    GLDashingLineEffect(const GrBackendProcessorFactory&, const GrProcessor&);
712
713    virtual void emitCode(const EmitArgs&) SK_OVERRIDE;
714
715    static inline void GenKey(const GrProcessor&, const GrGLCaps&, GrProcessorKeyBuilder*);
716
717    virtual void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
718
719private:
720    GrGLProgramDataManager::UniformHandle fRectUniform;
721    GrGLProgramDataManager::UniformHandle fIntervalUniform;
722    SkRect                                fPrevRect;
723    SkScalar                              fPrevIntervalLength;
724    typedef GrGLGeometryProcessor INHERITED;
725};
726
727GLDashingLineEffect::GLDashingLineEffect(const GrBackendProcessorFactory& factory,
728                                         const GrProcessor&)
729    : INHERITED (factory) {
730    fPrevRect.fLeft = SK_ScalarNaN;
731    fPrevIntervalLength = SK_ScalarMax;
732}
733
734void GLDashingLineEffect::emitCode(const EmitArgs& args) {
735    const DashingLineEffect& de = args.fGP.cast<DashingLineEffect>();
736    const char *rectName;
737    // The rect uniform's xyzw refer to (left + 0.5, top + 0.5, right - 0.5, bottom - 0.5),
738    // respectively.
739    fRectUniform = args.fPB->addUniform(GrGLProgramBuilder::kFragment_Visibility,
740                                       kVec4f_GrSLType,
741                                       "rect",
742                                       &rectName);
743    const char *intervalName;
744    // The interval uniform's refers to the total length of the interval (on + off)
745    fIntervalUniform = args.fPB->addUniform(GrGLProgramBuilder::kFragment_Visibility,
746                                            kFloat_GrSLType,
747                                            "interval",
748                                            &intervalName);
749
750    GrGLVertToFrag v(kVec2f_GrSLType);
751    args.fPB->addVarying("Coord", &v);
752    GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
753    vsBuilder->codeAppendf("\t%s = %s;\n", v.vsOut(), de.inCoord().c_str());
754
755    // setup position varying
756    vsBuilder->codeAppendf("%s = %s * vec3(%s, 1);", vsBuilder->glPosition(), vsBuilder->uViewM(),
757                           vsBuilder->inPosition());
758
759    // transforms all points so that we can compare them to our test rect
760    GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
761    fsBuilder->codeAppendf("\t\tfloat xShifted = %s.x - floor(%s.x / %s) * %s;\n",
762                           v.fsIn(), v.fsIn(), intervalName, intervalName);
763    fsBuilder->codeAppendf("\t\tvec2 fragPosShifted = vec2(xShifted, %s.y);\n", v.fsIn());
764    if (GrProcessorEdgeTypeIsAA(de.getEdgeType())) {
765        // The amount of coverage removed in x and y by the edges is computed as a pair of negative
766        // numbers, xSub and ySub.
767        fsBuilder->codeAppend("\t\tfloat xSub, ySub;\n");
768        fsBuilder->codeAppendf("\t\txSub = min(fragPosShifted.x - %s.x, 0.0);\n", rectName);
769        fsBuilder->codeAppendf("\t\txSub += min(%s.z - fragPosShifted.x, 0.0);\n", rectName);
770        fsBuilder->codeAppendf("\t\tySub = min(fragPosShifted.y - %s.y, 0.0);\n", rectName);
771        fsBuilder->codeAppendf("\t\tySub += min(%s.w - fragPosShifted.y, 0.0);\n", rectName);
772        // Now compute coverage in x and y and multiply them to get the fraction of the pixel
773        // covered.
774        fsBuilder->codeAppendf("\t\tfloat alpha = (1.0 + max(xSub, -1.0)) * (1.0 + max(ySub, -1.0));\n");
775    } else {
776        // Assuming the bounding geometry is tight so no need to check y values
777        fsBuilder->codeAppendf("\t\tfloat alpha = 1.0;\n");
778        fsBuilder->codeAppendf("\t\talpha *= (fragPosShifted.x - %s.x) > -0.5 ? 1.0 : 0.0;\n", rectName);
779        fsBuilder->codeAppendf("\t\talpha *= (%s.z - fragPosShifted.x) >= -0.5 ? 1.0 : 0.0;\n", rectName);
780    }
781    fsBuilder->codeAppendf("\t\t%s = %s;\n", args.fOutput,
782                           (GrGLSLExpr4(args.fInput) * GrGLSLExpr1("alpha")).c_str());
783}
784
785void GLDashingLineEffect::setData(const GrGLProgramDataManager& pdman,
786                                  const GrProcessor& processor) {
787    const DashingLineEffect& de = processor.cast<DashingLineEffect>();
788    const SkRect& rect = de.getRect();
789    SkScalar intervalLength = de.getIntervalLength();
790    if (rect != fPrevRect || intervalLength != fPrevIntervalLength) {
791        pdman.set4f(fRectUniform, rect.fLeft + 0.5f, rect.fTop + 0.5f,
792                    rect.fRight - 0.5f, rect.fBottom - 0.5f);
793        pdman.set1f(fIntervalUniform, intervalLength);
794        fPrevRect = rect;
795        fPrevIntervalLength = intervalLength;
796    }
797}
798
799void GLDashingLineEffect::GenKey(const GrProcessor& processor, const GrGLCaps&,
800                                 GrProcessorKeyBuilder* b) {
801    const DashingLineEffect& de = processor.cast<DashingLineEffect>();
802    b->add32(de.getEdgeType());
803}
804
805//////////////////////////////////////////////////////////////////////////////
806
807GrGeometryProcessor* DashingLineEffect::Create(GrPrimitiveEdgeType edgeType,
808                                               const DashInfo& info,
809                                               SkScalar strokeWidth) {
810    if (info.fCount != 2) {
811        return NULL;
812    }
813
814    return SkNEW_ARGS(DashingLineEffect, (edgeType, info, strokeWidth));
815}
816
817DashingLineEffect::~DashingLineEffect() {}
818
819void DashingLineEffect::onComputeInvariantOutput(GrInvariantOutput* inout) const {
820    inout->mulByUnknownAlpha();
821}
822
823const GrBackendGeometryProcessorFactory& DashingLineEffect::getFactory() const {
824    return GrTBackendGeometryProcessorFactory<DashingLineEffect>::getInstance();
825}
826
827DashingLineEffect::DashingLineEffect(GrPrimitiveEdgeType edgeType, const DashInfo& info,
828                                     SkScalar strokeWidth)
829    : fEdgeType(edgeType)
830    , fInCoord(this->addVertexAttrib(GrShaderVar("inCoord",
831                                                 kVec2f_GrSLType,
832                                                 GrShaderVar::kAttribute_TypeModifier))) {
833    SkScalar onLen = info.fIntervals[0];
834    SkScalar offLen = info.fIntervals[1];
835    SkScalar halfOffLen = SkScalarHalf(offLen);
836    SkScalar halfStroke = SkScalarHalf(strokeWidth);
837    fIntervalLength = onLen + offLen;
838    fRect.set(halfOffLen, -halfStroke, halfOffLen + onLen, halfStroke);
839}
840
841bool DashingLineEffect::onIsEqual(const GrGeometryProcessor& other) const {
842    const DashingLineEffect& de = other.cast<DashingLineEffect>();
843    return (fEdgeType == de.fEdgeType &&
844            fRect == de.fRect &&
845            fIntervalLength == de.fIntervalLength);
846}
847
848GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DashingLineEffect);
849
850GrGeometryProcessor* DashingLineEffect::TestCreate(SkRandom* random,
851                                                   GrContext*,
852                                                   const GrDrawTargetCaps& caps,
853                                                   GrTexture*[]) {
854    GrPrimitiveEdgeType edgeType = static_cast<GrPrimitiveEdgeType>(random->nextULessThan(
855            kGrProcessorEdgeTypeCnt));
856    SkScalar strokeWidth = random->nextRangeScalar(0, 100.f);
857    DashInfo info;
858    info.fCount = 2;
859    SkAutoTArray<SkScalar> intervals(info.fCount);
860    info.fIntervals = intervals.get();
861    info.fIntervals[0] = random->nextRangeScalar(0, 10.f);
862    info.fIntervals[1] = random->nextRangeScalar(0, 10.f);
863    info.fPhase = random->nextRangeScalar(0, info.fIntervals[0] + info.fIntervals[1]);
864
865    return DashingLineEffect::Create(edgeType, info, strokeWidth);
866}
867
868//////////////////////////////////////////////////////////////////////////////
869
870GrGeometryProcessor* GrDashingEffect::Create(GrPrimitiveEdgeType edgeType,
871                                             const SkPathEffect::DashInfo& info,
872                                             SkScalar strokeWidth,
873                                             GrDashingEffect::DashCap cap) {
874    switch (cap) {
875        case GrDashingEffect::kRound_DashCap:
876            return DashingCircleEffect::Create(edgeType, info, SkScalarHalf(strokeWidth));
877        case GrDashingEffect::kNonRound_DashCap:
878            return DashingLineEffect::Create(edgeType, info, strokeWidth);
879        default:
880            SkFAIL("Unexpected dashed cap.");
881    }
882    return NULL;
883}
884