1/* 2 * Copyright 2014 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#include "src/core/SkPathPriv.h" 9#include "src/gpu/effects/GrConvexPolyEffect.h" 10#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h" 11#include "src/gpu/glsl/GrGLSLProgramDataManager.h" 12#include "src/gpu/glsl/GrGLSLUniformHandler.h" 13#include "src/sksl/dsl/priv/DSLFPs.h" 14 15////////////////////////////////////////////////////////////////////////////// 16 17GrFPResult GrConvexPolyEffect::Make(std::unique_ptr<GrFragmentProcessor> inputFP, 18 GrClipEdgeType type, const SkPath& path) { 19 if (path.getSegmentMasks() != SkPath::kLine_SegmentMask || !path.isConvex()) { 20 return GrFPFailure(std::move(inputFP)); 21 } 22 23 SkPathFirstDirection dir = SkPathPriv::ComputeFirstDirection(path); 24 // The only way this should fail is if the clip is effectively a infinitely thin line. In that 25 // case nothing is inside the clip. It'd be nice to detect this at a higher level and either 26 // skip the draw or omit the clip element. 27 if (dir == SkPathFirstDirection::kUnknown) { 28 if (GrClipEdgeTypeIsInverseFill(type)) { 29 return GrFPSuccess( 30 GrFragmentProcessor::ModulateRGBA(std::move(inputFP), SK_PMColor4fWHITE)); 31 } 32 // This could use ConstColor instead of ModulateRGBA but it would trigger a debug print 33 // about a coverage processor not being compatible with the alpha-as-coverage optimization. 34 // We don't really care about this unlikely case so we just use ModulateRGBA to suppress 35 // the print. 36 return GrFPSuccess( 37 GrFragmentProcessor::ModulateRGBA(std::move(inputFP), SK_PMColor4fTRANSPARENT)); 38 } 39 40 SkScalar edges[3 * kMaxEdges]; 41 SkPoint pts[4]; 42 SkPath::Verb verb; 43 SkPath::Iter iter(path, true); 44 45 // SkPath considers itself convex so long as there is a convex contour within it, 46 // regardless of any degenerate contours such as a string of moveTos before it. 47 // Iterate here to consume any degenerate contours and only process the points 48 // on the actual convex contour. 49 int n = 0; 50 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { 51 switch (verb) { 52 case SkPath::kMove_Verb: 53 case SkPath::kClose_Verb: 54 break; 55 case SkPath::kLine_Verb: { 56 if (n >= kMaxEdges) { 57 return GrFPFailure(std::move(inputFP)); 58 } 59 if (pts[0] != pts[1]) { 60 SkVector v = pts[1] - pts[0]; 61 v.normalize(); 62 if (SkPathFirstDirection::kCCW == dir) { 63 edges[3 * n] = v.fY; 64 edges[3 * n + 1] = -v.fX; 65 } else { 66 edges[3 * n] = -v.fY; 67 edges[3 * n + 1] = v.fX; 68 } 69 edges[3 * n + 2] = -(edges[3 * n] * pts[1].fX + edges[3 * n + 1] * pts[1].fY); 70 ++n; 71 } 72 break; 73 } 74 default: 75 // Non-linear segment so not a polygon. 76 return GrFPFailure(std::move(inputFP)); 77 } 78 } 79 80 if (path.isInverseFillType()) { 81 type = GrInvertClipEdgeType(type); 82 } 83 return GrConvexPolyEffect::Make(std::move(inputFP), type, n, edges); 84} 85 86GrConvexPolyEffect::~GrConvexPolyEffect() {} 87 88SkString GrConvexPolyEffect::getShaderDfxInfo() const 89{ 90 SkString format; 91 format.printf("ShaderDfx_GrConvexPolyEffect_%d_%d", fEdgeCount, fEdgeType); 92 return format; 93} 94 95void GrConvexPolyEffect::onAddToKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const { 96 static_assert(kGrClipEdgeTypeCnt <= 8); 97 uint32_t key = (fEdgeCount << 3) | static_cast<int>(fEdgeType); 98 b->add32(key); 99} 100 101std::unique_ptr<GrFragmentProcessor::ProgramImpl> GrConvexPolyEffect::onMakeProgramImpl() const { 102 class Impl : public ProgramImpl { 103 public: 104 void emitCode(EmitArgs& args) override { 105 const GrConvexPolyEffect& cpe = args.fFp.cast<GrConvexPolyEffect>(); 106 107 using namespace SkSL::dsl; 108 StartFragmentProcessor(this, &args); 109 GlobalVar edgeArray(kUniform_Modifier, Array(kHalf3_Type, cpe.fEdgeCount), "edgeArray"); 110 Declare(edgeArray); 111 fEdgeUniform = VarUniformHandle(edgeArray); 112 Var alpha(kHalf_Type, "alpha", 1); 113 Declare(alpha); 114 Var edge(kHalf_Type, "edge"); 115 Declare(edge); 116 for (int i = 0; i < cpe.fEdgeCount; ++i) { 117 edge = Dot(edgeArray[i], Half3(Swizzle(sk_FragCoord(), X, Y, ONE))); 118 if (GrClipEdgeTypeIsAA(cpe.fEdgeType)) { 119 edge = Saturate(edge); 120 } else { 121 edge = Select(edge >= 0.5, 1.0, 0.0); 122 } 123 alpha *= edge; 124 } 125 126 if (GrClipEdgeTypeIsInverseFill(cpe.fEdgeType)) { 127 alpha = 1.0 - alpha; 128 } 129 130 Return(SampleChild(0) * alpha); 131 EndFragmentProcessor(); 132 } 133 134 private: 135 void onSetData(const GrGLSLProgramDataManager& pdman, 136 const GrFragmentProcessor& fp) override { 137 const GrConvexPolyEffect& cpe = fp.cast<GrConvexPolyEffect>(); 138 size_t n = 3*cpe.fEdgeCount; 139 if (!std::equal(fPrevEdges.begin(), fPrevEdges.begin() + n, cpe.fEdges.begin())) { 140 pdman.set3fv(fEdgeUniform, cpe.fEdgeCount, cpe.fEdges.data()); 141 std::copy_n(cpe.fEdges.begin(), n, fPrevEdges.begin()); 142 } 143 } 144 145 GrGLSLProgramDataManager::UniformHandle fEdgeUniform; 146 std::array<float, 3 * GrConvexPolyEffect::kMaxEdges> fPrevEdges = {SK_FloatNaN}; 147 }; 148 149 return std::make_unique<Impl>(); 150} 151 152GrConvexPolyEffect::GrConvexPolyEffect(std::unique_ptr<GrFragmentProcessor> inputFP, 153 GrClipEdgeType edgeType, 154 int n, 155 const float edges[]) 156 : INHERITED(kGrConvexPolyEffect_ClassID, 157 ProcessorOptimizationFlags(inputFP.get()) & 158 kCompatibleWithCoverageAsAlpha_OptimizationFlag) 159 , fEdgeType(edgeType) 160 , fEdgeCount(n) { 161 // Factory function should have already ensured this. 162 SkASSERT(n <= kMaxEdges); 163 std::copy_n(edges, 3*n, fEdges.begin()); 164 // Outset the edges by 0.5 so that a pixel with center on an edge is 50% covered in the AA case 165 // and 100% covered in the non-AA case. 166 for (int i = 0; i < n; ++i) { 167 fEdges[3 * i + 2] += SK_ScalarHalf; 168 } 169 170 this->registerChild(std::move(inputFP)); 171} 172 173GrConvexPolyEffect::GrConvexPolyEffect(const GrConvexPolyEffect& that) 174 : INHERITED(that) 175 , fEdgeType(that.fEdgeType) 176 , fEdgeCount(that.fEdgeCount) { 177 std::copy_n(that.fEdges.begin(), 3*that.fEdgeCount, fEdges.begin()); 178} 179 180std::unique_ptr<GrFragmentProcessor> GrConvexPolyEffect::clone() const { 181 return std::unique_ptr<GrFragmentProcessor>(new GrConvexPolyEffect(*this)); 182} 183 184bool GrConvexPolyEffect::onIsEqual(const GrFragmentProcessor& other) const { 185 const GrConvexPolyEffect& cpe = other.cast<GrConvexPolyEffect>(); 186 int n = 3*cpe.fEdgeCount; 187 return cpe.fEdgeType == fEdgeType && 188 cpe.fEdgeCount == fEdgeCount && 189 std::equal(cpe.fEdges.begin(), cpe.fEdges.begin() + n, fEdges.begin()); 190} 191 192////////////////////////////////////////////////////////////////////////////// 193 194GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrConvexPolyEffect); 195 196#if GR_TEST_UTILS 197std::unique_ptr<GrFragmentProcessor> GrConvexPolyEffect::TestCreate(GrProcessorTestData* d) { 198 int count = d->fRandom->nextULessThan(kMaxEdges) + 1; 199 SkScalar edges[kMaxEdges * 3]; 200 for (int i = 0; i < 3 * count; ++i) { 201 edges[i] = d->fRandom->nextSScalar1(); 202 } 203 204 bool success; 205 std::unique_ptr<GrFragmentProcessor> fp = d->inputFP(); 206 do { 207 GrClipEdgeType edgeType = 208 static_cast<GrClipEdgeType>(d->fRandom->nextULessThan(kGrClipEdgeTypeCnt)); 209 std::tie(success, fp) = GrConvexPolyEffect::Make(std::move(fp), edgeType, count, edges); 210 } while (!success); 211 return fp; 212} 213#endif 214