51 if (pts[1] == pts[0]) {
65 *parallelScale = vecSrc.
length();
66 *perpScale = vecSrcPerp.
length();
73 if (pts[1] == pts[0]) {
84 ptsRot[1].fY = pts[0].fY;
90 SkASSERT(phase < intervals[0] + intervals[1]);
91 if (phase >= intervals[0] && phase != 0) {
92 SkScalar srcIntervalLen = intervals[0] + intervals[1];
93 return srcIntervalLen - phase;
100 if (pts[1].fX <= pts[0].fX) {
103 SkScalar srcIntervalLen = intervals[0] + intervals[1];
105 SkScalar temp = totalLen / srcIntervalLen;
107 *endingInt = totalLen - numFullIntervals * srcIntervalLen + phase;
108 temp = *endingInt / srcIntervalLen;
110 if (0 == *endingInt) {
111 *endingInt = srcIntervalLen;
113 if (*endingInt > intervals[0]) {
114 return *endingInt - intervals[0];
124void setup_dashed_rect(
const SkRect& rect,
135 SkScalar intervalLength = startInterval + endInterval;
139 SkScalar halfDevRectHeight = rect.height() * perpScale / 2.f;
140 SkRect dashRect = {
offset - bloatX, -halfDevRectHeight,
141 offset + len + bloatX, halfDevRectHeight };
143 if (kRound_DashCap == cap) {
157 rectParam.
setLTRB(halfOffLen + 0.5f, -halfStroke + 0.5f,
158 halfOffLen + startInterval - 0.5f, halfStroke - 0.5f);
178 bool usesLocalCoords);
197 const LineData& geometry,
199 AAMode aaMode,
bool fullDash,
201 return GrOp::Make<DashOpImpl>(context, std::move(
paint), geometry, cap,
202 aaMode, fullDash, stencilSettings);
205 const char*
name()
const override {
return "DashOp"; }
215 FixedFunctionFlags fixedFunctionFlags()
const override {
216 FixedFunctionFlags
flags = FixedFunctionFlags::kNone;
217 if (AAMode::kCoverageWithMSAA == fAAMode) {
218 flags |= FixedFunctionFlags::kUsesHWAA;
221 flags |= FixedFunctionFlags::kUsesStencil;
229 auto analysis = fProcessorSet.
finalize(fColor, coverage,
clip, fStencilSettings, caps,
231 fUsesLocalCoords = analysis.usesLocalCoords();
241 , fColor(
paint.getColor4f())
242 , fFullDash(fullDash)
246 , fStencilSettings(stencilSettings) {
247 fLines.push_back(geometry);
250 SkScalar halfStrokeWidth = 0.5f * geometry.fSrcStrokeWidth;
253 bounds.set(geometry.fPtsRot[0], geometry.fPtsRot[1]);
254 bounds.outset(xBloat, halfStrokeWidth);
257 SkMatrix& combinedMatrix = fLines[0].fSrcRotInv;
258 combinedMatrix.
postConcat(geometry.fViewMatrix);
260 IsHairline zeroArea = geometry.fSrcStrokeWidth ? IsHairline::kNo : IsHairline::kYes;
261 HasAABloat aaBloat = (aaMode == AAMode::kNone) ? HasAABloat::kNo : HasAABloat::
kYes;
262 this->setTransformedBounds(bounds, combinedMatrix, aaBloat, zeroArea);
266 DashDraw(
const LineData& geo) {
267 memcpy(fPtsRot, geo.fPtsRot,
sizeof(geo.fPtsRot));
268 memcpy(fIntervals, geo.fIntervals,
sizeof(geo.fIntervals));
284 GrProgramInfo* programInfo()
override {
return fProgramInfo; }
286 void onCreateProgramInfo(
const GrCaps* caps,
289 bool usesMSAASurface,
295 DashCap capType = (this->cap() ==
SkPaint::kRound_Cap) ? kRound_DashCap : kNonRound_DashCap;
298 if (this->fullDash()) {
299 gp = make_dash_gp(arena, this->
color(), this->aaMode(), capType,
300 this->viewMatrix(), fUsesLocalCoords);
306 fUsesLocalCoords ? LocalCoords::kUsePosition_Type : LocalCoords::kUnused_Type;
309 Coverage::kSolid_Type,
315 SkDebugf(
"Could not create GrGeometryProcessor\n");
323 std::move(appliedClip),
326 std::move(fProcessorSet),
328 renderPassXferBarriers,
335 int instanceCount = fLines.size();
340 this->createProgramInfo(
target);
347 bool useAA = this->aaMode() != AAMode::kNone;
348 bool fullDash = this->fullDash();
353 static const int kNumStackDashes = 128;
357 int totalRectCount = 0;
360 for (
int i = 0; i < instanceCount; i++) {
361 const LineData&
args = fLines[i];
368 if (halfSrcStroke == 0.0f || this->aaMode() != AAMode::kCoverageWithMSAA) {
372 halfSrcStroke = std::max(halfSrcStroke, 0.5f /
args.fPerpendicularScale);
375 SkScalar strokeAdj = hasCap ? halfSrcStroke : 0.0f;
378 bool lineDone =
false;
383 SkRect& startRect = rects[rectOffset++];
384 SkRect& endRect = rects[rectOffset++];
386 bool hasStartRect =
false;
390 if (
draw.fPhase > 0 &&
draw.fPhase <
draw.fIntervals[0]) {
392 startPts[0] =
draw.fPtsRot[0];
393 startPts[1].
fY = startPts[0].
fY;
394 startPts[1].
fX = std::min(startPts[0].fX +
draw.fIntervals[0] -
draw.fPhase,
397 startRect.
outset(strokeAdj, halfSrcStroke);
400 startAdj =
draw.fIntervals[0] +
draw.fIntervals[1] -
draw.fPhase;
406 startAdj += calc_start_adjustment(
draw.fIntervals,
draw.fPhase);
408 draw.fPtsRot[0].fX += startAdj;
414 draw.fPtsRot[1].fX -= endAdj;
415 if (
draw.fPtsRot[0].fX >=
draw.fPtsRot[1].fX) {
419 bool hasEndRect =
false;
422 if (useAA && !lineDone) {
426 if (0 == endAdj && endingInterval !=
draw.fIntervals[0]) {
428 endPts[1] =
draw.fPtsRot[1];
429 endPts[0].
fY = endPts[1].
fY;
430 endPts[0].
fX = endPts[1].
fX - endingInterval;
433 endRect.
outset(strokeAdj, halfSrcStroke);
436 endAdj = endingInterval +
draw.fIntervals[1];
438 draw.fPtsRot[1].fX -= endAdj;
439 if (
draw.fPtsRot[0].fX >=
draw.fPtsRot[1].fX) {
445 if (
draw.fPtsRot[0].fX ==
draw.fPtsRot[1].fX &&
446 (0 != endAdj || 0 == startAdj) &&
464 devIntervals[0] =
draw.fIntervals[0] *
args.fParallelScale;
465 devIntervals[1] =
draw.fIntervals[1] *
args.fParallelScale;
480 SkScalar startOffset = devIntervals[1] * 0.5f + devPhase;
484 switch (this->aaMode()) {
487 case AAMode::kCoverage:
492 case AAMode::kCoverageWithMSAA:
501 if (devIntervals[1] <= 0.f && useAA) {
506 draw.fPtsRot[0].fX -= hasStartRect ? startAdj : 0;
507 draw.fPtsRot[1].fX += hasEndRect ? endAdj : 0;
509 startRect.
outset(strokeAdj, halfSrcStroke);
515 args.fSrcRotInv.mapPoints(devicePts,
draw.fPtsRot, 2);
518 lineLength += 2.f * halfDevStroke;
520 devIntervals[0] = lineLength;
523 totalRectCount += !lineDone ? 1 : 0;
524 totalRectCount += hasStartRect ? 1 : 0;
525 totalRectCount += hasEndRect ? 1 : 0;
530 startOffset -= halfDevStroke;
535 args.fSrcRotInv.mapPoints(devicePts,
draw.fPtsRot, 2);
538 draw.fLineLength += 2.f * halfDevStroke;
542 draw.fPtsRot[1].fX,
draw.fPtsRot[1].fY);
543 bounds.outset(bloatX + strokeAdj, bloatY + halfSrcStroke);
548 startRect.
outset(bloatX, bloatY);
553 endRect.
outset(bloatX, bloatY);
556 draw.fStartOffset = startOffset;
557 draw.fDevBloatX = devBloatX;
558 draw.fPerpendicularScale =
args.fPerpendicularScale;
560 draw.fHasStartRect = hasStartRect;
561 draw.fLineDone = lineDone;
562 draw.fHasEndRect = hasEndRect;
565 if (!totalRectCount) {
570 VertexWriter vertices{ helper.vertices() };
576 for (
int i = 0; i < instanceCount; i++) {
577 const LineData& geom = fLines[i];
581 setup_dashed_rect(rects[rectIndex], vertices, geom.fSrcRotInv,
582 draws[i].fStartOffset, draws[i].fDevBloatX,
583 draws[i].fLineLength, draws[i].fIntervals[0],
584 draws[i].fIntervals[1], draws[i].fStrokeWidth,
585 draws[i].fPerpendicularScale,
595 setup_dashed_rect(rects[rectIndex], vertices, geom.fSrcRotInv,
596 draws[i].fStartOffset, draws[i].fDevBloatX,
597 draws[i].fIntervals[0], draws[i].fIntervals[0],
598 draws[i].fIntervals[1], draws[i].fStrokeWidth,
599 draws[i].fPerpendicularScale, capType);
608 setup_dashed_rect(rects[rectIndex], vertices, geom.fSrcRotInv,
609 draws[i].fStartOffset, draws[i].fDevBloatX,
610 draws[i].fIntervals[0], draws[i].fIntervals[0],
611 draws[i].fIntervals[1], draws[i].fStrokeWidth,
612 draws[i].fPerpendicularScale, capType);
620 fMesh = helper.mesh();
624 if (!fProgramInfo || !fMesh) {
634 auto that = t->
cast<DashOpImpl>();
635 if (fProcessorSet != that->fProcessorSet) {
636 return CombineResult::kCannotCombine;
639 if (this->aaMode() != that->aaMode()) {
640 return CombineResult::kCannotCombine;
643 if (this->fullDash() != that->fullDash()) {
644 return CombineResult::kCannotCombine;
647 if (this->cap() != that->cap()) {
648 return CombineResult::kCannotCombine;
652 if (this->
color() != that->color()) {
653 return CombineResult::kCannotCombine;
657 return CombineResult::kCannotCombine;
660 fLines.push_back_n(that->fLines.size(), that->fLines.begin());
661 return CombineResult::kMerged;
664#if defined(GR_TEST_UTILS)
665 SkString onDumpInfo()
const override {
667 for (
const auto& geo : fLines) {
668 string.
appendf(
"Pt0: [%.2f, %.2f], Pt1: [%.2f, %.2f], Width: %.2f, Ival0: %.2f, "
669 "Ival1 : %.2f, Phase: %.2f\n",
670 geo.fPtsRot[0].fX, geo.fPtsRot[0].fY,
671 geo.fPtsRot[1].fX, geo.fPtsRot[1].fY,
677 string += fProcessorSet.dumpProcessors();
683 const SkMatrix& viewMatrix()
const {
return fLines[0].fViewMatrix; }
684 AAMode aaMode()
const {
return fAAMode; }
685 bool fullDash()
const {
return fFullDash; }
690 bool fUsesLocalCoords : 1;
721 bool usesLocalCoords);
723 const char*
name()
const override {
return "DashingCircleEffect"; }
725 void addToKey(
const GrShaderCaps&, KeyBuilder*)
const override;
727 std::unique_ptr<ProgramImpl> makeProgramImpl(
const GrShaderCaps&)
const override;
733 bool usesLocalCoords);
737 bool fUsesLocalCoords;
751class DashingCircleEffect::Impl :
public ProgramImpl {
758 void onEmitCode(EmitArgs&, GrGPArgs*)
override;
763 UniformHandle fParamUniform;
764 UniformHandle fColorUniform;
765 UniformHandle fLocalMatrixUniform;
768void DashingCircleEffect::Impl::onEmitCode(EmitArgs&
args, GrGPArgs* gpArgs) {
769 const DashingCircleEffect& dce =
args.fGeomProc.cast<DashingCircleEffect>();
779 varyingHandler->
addVarying(
"DashParam", &dashParams);
780 vertBuilder->codeAppendf(
"%s = %s;", dashParams.vsOut(), dce.fInDashParams.name());
784 varyingHandler->
addVarying(
"CircleParams", &circleParams);
785 vertBuilder->codeAppendf(
"%s = %s;", circleParams.vsOut(), dce.fInCircleParams.name());
790 this->setupUniformColor(fragBuilder, uniformHandler,
args.fOutputColor, &fColorUniform);
793 WriteOutputPosition(vertBuilder, gpArgs, dce.fInPosition.name());
794 if (dce.fUsesLocalCoords) {
795 WriteLocalCoord(vertBuilder,
799 dce.fInPosition.asShaderVar(),
801 &fLocalMatrixUniform);
805 fragBuilder->
codeAppendf(
"half xShifted = half(%s.x - floor(%s.x / %s.z) * %s.z);",
806 dashParams.fsIn(), dashParams.fsIn(), dashParams.fsIn(),
808 fragBuilder->
codeAppendf(
"half2 fragPosShifted = half2(xShifted, half(%s.y));",
810 fragBuilder->
codeAppendf(
"half2 center = half2(%s.y, 0.0);", circleParams.fsIn());
811 fragBuilder->
codeAppend(
"half dist = length(center - fragPosShifted);");
812 if (dce.fAAMode != AAMode::kNone) {
813 fragBuilder->
codeAppendf(
"half diff = dist - %s.x;", circleParams.fsIn());
814 fragBuilder->
codeAppend(
"diff = 1.0 - diff;");
815 fragBuilder->
codeAppend(
"half alpha = saturate(diff);");
818 fragBuilder->
codeAppendf(
"alpha *= dist < %s.x + 0.5 ? 1.0 : 0.0;", circleParams.fsIn());
820 fragBuilder->
codeAppendf(
"half4 %s = half4(alpha);",
args.fOutputCoverage);
826 const DashingCircleEffect& dce = geomProc.
cast<DashingCircleEffect>();
827 if (dce.fColor != fColor) {
828 pdman.
set4fv(fColorUniform, 1, dce.fColor.vec());
831 SetTransform(pdman, shaderCaps, fLocalMatrixUniform, dce.fLocalMatrix, &fLocalMatrix);
840 bool usesLocalCoords) {
841 return arena->
make([&](
void* ptr) {
842 return new (ptr) DashingCircleEffect(
color, aaMode, localMatrix, usesLocalCoords);
846void DashingCircleEffect::addToKey(
const GrShaderCaps& caps, KeyBuilder*
b)
const {
848 key |= fUsesLocalCoords ? 0x1 : 0x0;
849 key |=
static_cast<uint32_t
>(fAAMode) << 1;
850 key |= ProgramImpl::ComputeMatrixKey(caps, fLocalMatrix) << 3;
854std::unique_ptr<GrGeometryProcessor::ProgramImpl> DashingCircleEffect::makeProgramImpl(
856 return std::make_unique<Impl>();
862 bool usesLocalCoords)
863 :
INHERITED(kDashingCircleEffect_ClassID)
865 , fLocalMatrix(localMatrix)
866 , fUsesLocalCoords(usesLocalCoords)
871 this->setVertexAttributesWithImplicitOffsets(&fInPosition, 3);
876#if defined(GR_TEST_UTILS)
878 AAMode aaMode =
static_cast<AAMode>(
d->fRandom->nextULessThan(kAAModeCnt));
881 return DashingCircleEffect::Make(
d->allocator(),
885 d->fRandom->nextBool());
908 bool usesLocalCoords);
910 const char*
name()
const override {
return "DashingEffect"; }
912 bool usesLocalCoords()
const {
return fUsesLocalCoords; }
922 bool usesLocalCoords);
926 bool fUsesLocalCoords;
940class DashingLineEffect::Impl :
public ProgramImpl {
947 void onEmitCode(EmitArgs&, GrGPArgs*)
override;
952 UniformHandle fLocalMatrixUniform;
953 UniformHandle fColorUniform;
956void DashingLineEffect::Impl::onEmitCode(EmitArgs&
args, GrGPArgs* gpArgs) {
957 const DashingLineEffect&
de =
args.fGeomProc.cast<DashingLineEffect>();
968 varyingHandler->
addVarying(
"DashParams", &inDashParams);
969 vertBuilder->codeAppendf(
"%s = %s;", inDashParams.vsOut(),
de.fInDashParams.name());
974 varyingHandler->
addVarying(
"RectParams", &inRectParams);
975 vertBuilder->codeAppendf(
"%s = %s;", inRectParams.vsOut(),
de.fInRect.name());
980 this->setupUniformColor(fragBuilder, uniformHandler,
args.fOutputColor, &fColorUniform);
983 WriteOutputPosition(vertBuilder, gpArgs,
de.fInPosition.name());
984 if (
de.usesLocalCoords()) {
985 WriteLocalCoord(vertBuilder,
989 de.fInPosition.asShaderVar(),
991 &fLocalMatrixUniform);
995 fragBuilder->
codeAppendf(
"half xShifted = half(%s.x - floor(%s.x / %s.z) * %s.z);",
996 inDashParams.fsIn(), inDashParams.fsIn(), inDashParams.fsIn(),
997 inDashParams.fsIn());
998 fragBuilder->
codeAppendf(
"half2 fragPosShifted = half2(xShifted, half(%s.y));",
999 inDashParams.fsIn());
1000 if (
de.fAAMode == AAMode::kCoverage) {
1004 fragBuilder->
codeAppendf(
"xSub = half(min(fragPosShifted.x - %s.x, 0.0));",
1005 inRectParams.fsIn());
1006 fragBuilder->
codeAppendf(
"xSub += half(min(%s.z - fragPosShifted.x, 0.0));",
1007 inRectParams.fsIn());
1008 fragBuilder->
codeAppendf(
"ySub = half(min(fragPosShifted.y - %s.y, 0.0));",
1009 inRectParams.fsIn());
1010 fragBuilder->
codeAppendf(
"ySub += half(min(%s.w - fragPosShifted.y, 0.0));",
1011 inRectParams.fsIn());
1015 "half alpha = (1.0 + max(xSub, -1.0)) * (1.0 + max(ySub, -1.0));");
1016 }
else if (
de.fAAMode == AAMode::kCoverageWithMSAA) {
1020 fragBuilder->
codeAppendf(
"xSub = half(min(fragPosShifted.x - %s.x, 0.0));",
1021 inRectParams.fsIn());
1022 fragBuilder->
codeAppendf(
"xSub += half(min(%s.z - fragPosShifted.x, 0.0));",
1023 inRectParams.fsIn());
1025 fragBuilder->
codeAppendf(
"half alpha = (1.0 + max(xSub, -1.0));");
1029 fragBuilder->
codeAppendf(
"alpha *= (fragPosShifted.x - %s.x) > -0.5 ? 1.0 : 0.0;",
1030 inRectParams.fsIn());
1031 fragBuilder->
codeAppendf(
"alpha *= (%s.z - fragPosShifted.x) >= -0.5 ? 1.0 : 0.0;",
1032 inRectParams.fsIn());
1034 fragBuilder->
codeAppendf(
"half4 %s = half4(alpha);",
args.fOutputCoverage);
1040 const DashingLineEffect&
de = geomProc.
cast<DashingLineEffect>();
1041 if (
de.fColor != fColor) {
1042 pdman.
set4fv(fColorUniform, 1,
de.fColor.vec());
1045 SetTransform(pdman, shaderCaps, fLocalMatrixUniform,
de.fLocalMatrix, &fLocalMatrix);
1054 bool usesLocalCoords) {
1055 return arena->
make([&](
void* ptr) {
1056 return new (ptr) DashingLineEffect(
color, aaMode, localMatrix, usesLocalCoords);
1060void DashingLineEffect::addToKey(
const GrShaderCaps& caps, KeyBuilder*
b)
const {
1062 key |= fUsesLocalCoords ? 0x1 : 0x0;
1063 key |=
static_cast<int>(fAAMode) << 1;
1064 key |= ProgramImpl::ComputeMatrixKey(caps, fLocalMatrix) << 3;
1068std::unique_ptr<GrGeometryProcessor::ProgramImpl> DashingLineEffect::makeProgramImpl(
1070 return std::make_unique<Impl>();
1076 bool usesLocalCoords)
1079 , fLocalMatrix(localMatrix)
1080 , fUsesLocalCoords(usesLocalCoords)
1085 this->setVertexAttributesWithImplicitOffsets(&fInPosition, 3);
1090#if defined(GR_TEST_UTILS)
1092 AAMode aaMode =
static_cast<AAMode>(
d->fRandom->nextULessThan(kAAModeCnt));
1095 return DashingLineEffect::Make(
d->allocator(),
1099 d->fRandom->nextBool());
1110 bool usesLocalCoords) {
1118 case kRound_DashCap:
1119 return DashingCircleEffect::Make(arena,
color, aaMode,
invert, usesLocalCoords);
1120 case kNonRound_DashCap:
1121 return DashingLineEffect::Make(arena,
color, aaMode,
invert, usesLocalCoords);
1143 DashOpImpl::LineData lineData;
1147 SkASSERT(phase >= 0 && phase < intervals[0] + intervals[1]);
1150 if (pts[0].fY != pts[1].fY || pts[0].fX > pts[1].fX) {
1152 align_to_x_axis(pts, &rotMatrix, lineData.fPtsRot);
1153 if (!rotMatrix.
invert(&lineData.fSrcRotInv)) {
1154 SkDebugf(
"Failed to create invertible rotation matrix!\n");
1158 lineData.fSrcRotInv.reset();
1159 memcpy(lineData.fPtsRot, pts, 2 *
sizeof(
SkPoint));
1163 calc_dash_scaling(&lineData.fParallelScale, &lineData.fPerpendicularScale, viewMatrix, pts);
1169 SkScalar offInterval = intervals[1] * lineData.fParallelScale;
1178 bool fullDash = offInterval > 0.f || aaMode != AAMode::kNone;
1180 lineData.fViewMatrix = viewMatrix;
1181 lineData.fPhase = phase;
1182 lineData.fIntervals[0] = intervals[0];
1183 lineData.fIntervals[1] = intervals[1];
1185 return DashOpImpl::Make(context, std::move(
paint), lineData, cap, aaMode, fullDash,
1192 if (pts[0].fX != pts[1].fX && pts[0].fY != pts[1].fY) {
1207 if (0 == intervals[0] && 0 == intervals[1]) {
1214 if (intervals[0] != 0.f) {