1 /*
2  * Copyright 2013 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 "GrOvalRenderer.h"
9 
10 #include "GrBatchFlushState.h"
11 #include "GrBatchTest.h"
12 #include "GrGeometryProcessor.h"
13 #include "GrInvariantOutput.h"
14 #include "GrProcessor.h"
15 #include "GrResourceProvider.h"
16 #include "SkRRect.h"
17 #include "SkStrokeRec.h"
18 #include "batches/GrVertexBatch.h"
19 #include "glsl/GrGLSLFragmentShaderBuilder.h"
20 #include "glsl/GrGLSLGeometryProcessor.h"
21 #include "glsl/GrGLSLProgramDataManager.h"
22 #include "glsl/GrGLSLVarying.h"
23 #include "glsl/GrGLSLVertexShaderBuilder.h"
24 #include "glsl/GrGLSLUniformHandler.h"
25 #include "glsl/GrGLSLUtil.h"
26 
27 // TODO(joshualitt) - Break this file up during GrBatch post implementation cleanup
28 
29 namespace {
30 
31 struct CircleVertex {
32     SkPoint  fPos;
33     GrColor  fColor;
34     SkPoint  fOffset;
35     SkScalar fOuterRadius;
36     SkScalar fInnerRadius;
37 };
38 
39 struct EllipseVertex {
40     SkPoint  fPos;
41     GrColor  fColor;
42     SkPoint  fOffset;
43     SkPoint  fOuterRadii;
44     SkPoint  fInnerRadii;
45 };
46 
47 struct DIEllipseVertex {
48     SkPoint  fPos;
49     GrColor  fColor;
50     SkPoint  fOuterOffset;
51     SkPoint  fInnerOffset;
52 };
53 
circle_stays_circle(const SkMatrix & m)54 inline bool circle_stays_circle(const SkMatrix& m) {
55     return m.isSimilarity();
56 }
57 
58 }
59 
60 ///////////////////////////////////////////////////////////////////////////////
61 
62 /**
63  * The output of this effect is a modulation of the input color and coverage for a circle. It
64  * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
65  * with origin at the circle center. Two   vertex attributes are used:
66  *    vec2f : position in device space of the bounding geometry vertices
67  *    vec4f : (p.xy, outerRad, innerRad)
68  *             p is the position in the normalized space.
69  *             outerRad is the outerRadius in device space.
70  *             innerRad is the innerRadius in normalized space (ignored if not stroking).
71  */
72 
73 class CircleEdgeEffect : public GrGeometryProcessor {
74 public:
Create(GrColor color,bool stroke,const SkMatrix & localMatrix,bool usesLocalCoords)75     static GrGeometryProcessor* Create(GrColor color, bool stroke, const SkMatrix& localMatrix,
76                                        bool usesLocalCoords) {
77         return new CircleEdgeEffect(color, stroke, localMatrix, usesLocalCoords);
78     }
79 
inPosition() const80     const Attribute* inPosition() const { return fInPosition; }
inColor() const81     const Attribute* inColor() const { return fInColor; }
inCircleEdge() const82     const Attribute* inCircleEdge() const { return fInCircleEdge; }
color() const83     GrColor color() const { return fColor; }
colorIgnored() const84     bool colorIgnored() const { return GrColor_ILLEGAL == fColor; }
localMatrix() const85     const SkMatrix& localMatrix() const { return fLocalMatrix; }
usesLocalCoords() const86     bool usesLocalCoords() const { return fUsesLocalCoords; }
~CircleEdgeEffect()87     virtual ~CircleEdgeEffect() {}
88 
name() const89     const char* name() const override { return "CircleEdge"; }
90 
isStroked() const91     inline bool isStroked() const { return fStroke; }
92 
93     class GLSLProcessor : public GrGLSLGeometryProcessor {
94     public:
GLSLProcessor()95         GLSLProcessor() {}
96 
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)97         void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
98             const CircleEdgeEffect& ce = args.fGP.cast<CircleEdgeEffect>();
99             GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
100             GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
101             GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
102 
103             // emit attributes
104             varyingHandler->emitAttributes(ce);
105 
106             GrGLSLVertToFrag v(kVec4f_GrSLType);
107             varyingHandler->addVarying("CircleEdge", &v);
108             vertBuilder->codeAppendf("%s = %s;", v.vsOut(), ce.inCircleEdge()->fName);
109 
110             GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
111             // setup pass through color
112             if (!ce.colorIgnored()) {
113                 varyingHandler->addPassThroughAttribute(ce.inColor(), args.fOutputColor);
114             }
115 
116             // Setup position
117             this->setupPosition(vertBuilder, gpArgs, ce.inPosition()->fName);
118 
119             // emit transforms
120             this->emitTransforms(vertBuilder,
121                                  varyingHandler,
122                                  uniformHandler,
123                                  gpArgs->fPositionVar,
124                                  ce.inPosition()->fName,
125                                  ce.localMatrix(),
126                                  args.fTransformsIn,
127                                  args.fTransformsOut);
128 
129             fragBuilder->codeAppendf("float d = length(%s.xy);", v.fsIn());
130             fragBuilder->codeAppendf("float edgeAlpha = clamp(%s.z * (1.0 - d), 0.0, 1.0);",
131                                      v.fsIn());
132             if (ce.isStroked()) {
133                 fragBuilder->codeAppendf("float innerAlpha = clamp(%s.z * (d - %s.w), 0.0, 1.0);",
134                                          v.fsIn(), v.fsIn());
135                 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
136             }
137 
138             fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
139         }
140 
GenKey(const GrGeometryProcessor & gp,const GrGLSLCaps &,GrProcessorKeyBuilder * b)141         static void GenKey(const GrGeometryProcessor& gp,
142                            const GrGLSLCaps&,
143                            GrProcessorKeyBuilder* b) {
144             const CircleEdgeEffect& ce = gp.cast<CircleEdgeEffect>();
145             uint16_t key = ce.isStroked() ? 0x1 : 0x0;
146             key |= ce.usesLocalCoords() && ce.localMatrix().hasPerspective() ? 0x2 : 0x0;
147             key |= ce.colorIgnored() ? 0x4 : 0x0;
148             b->add32(key);
149         }
150 
setData(const GrGLSLProgramDataManager & pdman,const GrPrimitiveProcessor & gp)151         void setData(const GrGLSLProgramDataManager& pdman,
152                      const GrPrimitiveProcessor& gp) override {
153         }
154 
setTransformData(const GrPrimitiveProcessor & primProc,const GrGLSLProgramDataManager & pdman,int index,const SkTArray<const GrCoordTransform *,true> & transforms)155         void setTransformData(const GrPrimitiveProcessor& primProc,
156                               const GrGLSLProgramDataManager& pdman,
157                               int index,
158                               const SkTArray<const GrCoordTransform*, true>& transforms) override {
159             this->setTransformDataHelper<CircleEdgeEffect>(primProc, pdman, index, transforms);
160         }
161 
162     private:
163         typedef GrGLSLGeometryProcessor INHERITED;
164     };
165 
getGLSLProcessorKey(const GrGLSLCaps & caps,GrProcessorKeyBuilder * b) const166     void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
167         GLSLProcessor::GenKey(*this, caps, b);
168     }
169 
createGLSLInstance(const GrGLSLCaps &) const170     GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
171         return new GLSLProcessor();
172     }
173 
174 private:
CircleEdgeEffect(GrColor color,bool stroke,const SkMatrix & localMatrix,bool usesLocalCoords)175     CircleEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix, bool usesLocalCoords)
176         : fColor(color)
177         , fLocalMatrix(localMatrix)
178         , fUsesLocalCoords(usesLocalCoords) {
179         this->initClassID<CircleEdgeEffect>();
180         fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType,
181                                                        kHigh_GrSLPrecision));
182         fInColor = &this->addVertexAttrib(Attribute("inColor", kVec4ub_GrVertexAttribType));
183         fInCircleEdge = &this->addVertexAttrib(Attribute("inCircleEdge",
184                                                            kVec4f_GrVertexAttribType));
185         fStroke = stroke;
186     }
187 
188     GrColor fColor;
189     SkMatrix fLocalMatrix;
190     const Attribute* fInPosition;
191     const Attribute* fInColor;
192     const Attribute* fInCircleEdge;
193     bool fStroke;
194     bool fUsesLocalCoords;
195 
196     GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
197 
198     typedef GrGeometryProcessor INHERITED;
199 };
200 
201 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleEdgeEffect);
202 
TestCreate(GrProcessorTestData * d)203 const GrGeometryProcessor* CircleEdgeEffect::TestCreate(GrProcessorTestData* d) {
204     return CircleEdgeEffect::Create(GrRandomColor(d->fRandom),
205                                     d->fRandom->nextBool(),
206                                     GrTest::TestMatrix(d->fRandom),
207                                     d->fRandom->nextBool());
208 }
209 
210 ///////////////////////////////////////////////////////////////////////////////
211 
212 /**
213  * The output of this effect is a modulation of the input color and coverage for an axis-aligned
214  * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
215  * in both x and y directions.
216  *
217  * We are using an implicit function of x^2/a^2 + y^2/b^2 - 1 = 0.
218  */
219 
220 class EllipseEdgeEffect : public GrGeometryProcessor {
221 public:
Create(GrColor color,bool stroke,const SkMatrix & localMatrix,bool usesLocalCoords)222     static GrGeometryProcessor* Create(GrColor color, bool stroke, const SkMatrix& localMatrix,
223                                        bool usesLocalCoords) {
224         return new EllipseEdgeEffect(color, stroke, localMatrix, usesLocalCoords);
225     }
226 
~EllipseEdgeEffect()227     virtual ~EllipseEdgeEffect() {}
228 
name() const229     const char* name() const override { return "EllipseEdge"; }
230 
inPosition() const231     const Attribute* inPosition() const { return fInPosition; }
inColor() const232     const Attribute* inColor() const { return fInColor; }
inEllipseOffset() const233     const Attribute* inEllipseOffset() const { return fInEllipseOffset; }
inEllipseRadii() const234     const Attribute* inEllipseRadii() const { return fInEllipseRadii; }
color() const235     GrColor color() const { return fColor; }
colorIgnored() const236     bool colorIgnored() const { return GrColor_ILLEGAL == fColor; }
localMatrix() const237     const SkMatrix& localMatrix() const { return fLocalMatrix; }
usesLocalCoords() const238     bool usesLocalCoords() const { return fUsesLocalCoords; }
239 
isStroked() const240     inline bool isStroked() const { return fStroke; }
241 
242     class GLSLProcessor : public GrGLSLGeometryProcessor {
243     public:
GLSLProcessor()244         GLSLProcessor() {}
245 
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)246         void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
247             const EllipseEdgeEffect& ee = args.fGP.cast<EllipseEdgeEffect>();
248             GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
249             GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
250             GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
251 
252             // emit attributes
253             varyingHandler->emitAttributes(ee);
254 
255             GrGLSLVertToFrag ellipseOffsets(kVec2f_GrSLType);
256             varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
257             vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
258                                    ee.inEllipseOffset()->fName);
259 
260             GrGLSLVertToFrag ellipseRadii(kVec4f_GrSLType);
261             varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
262             vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(),
263                                    ee.inEllipseRadii()->fName);
264 
265             GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
266             // setup pass through color
267             if (!ee.colorIgnored()) {
268                 varyingHandler->addPassThroughAttribute(ee.inColor(), args.fOutputColor);
269             }
270 
271             // Setup position
272             this->setupPosition(vertBuilder, gpArgs, ee.inPosition()->fName);
273 
274             // emit transforms
275             this->emitTransforms(vertBuilder,
276                                  varyingHandler,
277                                  uniformHandler,
278                                  gpArgs->fPositionVar,
279                                  ee.inPosition()->fName,
280                                  ee.localMatrix(),
281                                  args.fTransformsIn,
282                                  args.fTransformsOut);
283 
284             // for outer curve
285             fragBuilder->codeAppendf("vec2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
286                                      ellipseRadii.fsIn());
287             fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
288             fragBuilder->codeAppendf("vec2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
289             fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
290 
291             // avoid calling inversesqrt on zero.
292             fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
293             fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
294             fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
295 
296             // for inner curve
297             if (ee.isStroked()) {
298                 fragBuilder->codeAppendf("scaledOffset = %s*%s.zw;",
299                                          ellipseOffsets.fsIn(), ellipseRadii.fsIn());
300                 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
301                 fragBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;",
302                                          ellipseRadii.fsIn());
303                 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
304                 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
305             }
306 
307             fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
308         }
309 
GenKey(const GrGeometryProcessor & gp,const GrGLSLCaps &,GrProcessorKeyBuilder * b)310         static void GenKey(const GrGeometryProcessor& gp,
311                            const GrGLSLCaps&,
312                            GrProcessorKeyBuilder* b) {
313             const EllipseEdgeEffect& ee = gp.cast<EllipseEdgeEffect>();
314             uint16_t key = ee.isStroked() ? 0x1 : 0x0;
315             key |= ee.usesLocalCoords() && ee.localMatrix().hasPerspective() ? 0x2 : 0x0;
316             key |= ee.colorIgnored() ? 0x4 : 0x0;
317             b->add32(key);
318         }
319 
setData(const GrGLSLProgramDataManager & pdman,const GrPrimitiveProcessor & gp)320         void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp) override {
321         }
322 
setTransformData(const GrPrimitiveProcessor & primProc,const GrGLSLProgramDataManager & pdman,int index,const SkTArray<const GrCoordTransform *,true> & transforms)323         void setTransformData(const GrPrimitiveProcessor& primProc,
324                               const GrGLSLProgramDataManager& pdman,
325                               int index,
326                               const SkTArray<const GrCoordTransform*, true>& transforms) override {
327             this->setTransformDataHelper<EllipseEdgeEffect>(primProc, pdman, index, transforms);
328         }
329 
330     private:
331         typedef GrGLSLGeometryProcessor INHERITED;
332     };
333 
getGLSLProcessorKey(const GrGLSLCaps & caps,GrProcessorKeyBuilder * b) const334     void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
335         GLSLProcessor::GenKey(*this, caps, b);
336     }
337 
createGLSLInstance(const GrGLSLCaps &) const338     GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
339         return new GLSLProcessor();
340     }
341 
342 private:
EllipseEdgeEffect(GrColor color,bool stroke,const SkMatrix & localMatrix,bool usesLocalCoords)343     EllipseEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix,
344                       bool usesLocalCoords)
345         : fColor(color)
346         , fLocalMatrix(localMatrix)
347         , fUsesLocalCoords(usesLocalCoords) {
348         this->initClassID<EllipseEdgeEffect>();
349         fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
350         fInColor = &this->addVertexAttrib(Attribute("inColor", kVec4ub_GrVertexAttribType));
351         fInEllipseOffset = &this->addVertexAttrib(Attribute("inEllipseOffset",
352                                                             kVec2f_GrVertexAttribType));
353         fInEllipseRadii = &this->addVertexAttrib(Attribute("inEllipseRadii",
354                                                            kVec4f_GrVertexAttribType));
355         fStroke = stroke;
356     }
357 
358     const Attribute* fInPosition;
359     const Attribute* fInColor;
360     const Attribute* fInEllipseOffset;
361     const Attribute* fInEllipseRadii;
362     GrColor fColor;
363     SkMatrix fLocalMatrix;
364     bool fStroke;
365     bool fUsesLocalCoords;
366 
367     GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
368 
369     typedef GrGeometryProcessor INHERITED;
370 };
371 
372 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseEdgeEffect);
373 
TestCreate(GrProcessorTestData * d)374 const GrGeometryProcessor* EllipseEdgeEffect::TestCreate(GrProcessorTestData* d) {
375     return EllipseEdgeEffect::Create(GrRandomColor(d->fRandom),
376                                      d->fRandom->nextBool(),
377                                      GrTest::TestMatrix(d->fRandom),
378                                      d->fRandom->nextBool());
379 }
380 
381 ///////////////////////////////////////////////////////////////////////////////
382 
383 /**
384  * The output of this effect is a modulation of the input color and coverage for an ellipse,
385  * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
386  * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
387  * using differentials.
388  *
389  * The result is device-independent and can be used with any affine matrix.
390  */
391 
392 class DIEllipseEdgeEffect : public GrGeometryProcessor {
393 public:
394     enum Mode { kStroke = 0, kHairline, kFill };
395 
Create(GrColor color,const SkMatrix & viewMatrix,Mode mode,bool usesLocalCoords)396     static GrGeometryProcessor* Create(GrColor color, const SkMatrix& viewMatrix, Mode mode,
397                                        bool usesLocalCoords) {
398         return new DIEllipseEdgeEffect(color, viewMatrix, mode, usesLocalCoords);
399     }
400 
~DIEllipseEdgeEffect()401     virtual ~DIEllipseEdgeEffect() {}
402 
name() const403     const char* name() const override { return "DIEllipseEdge"; }
404 
inPosition() const405     const Attribute* inPosition() const { return fInPosition; }
inColor() const406     const Attribute* inColor() const { return fInColor; }
inEllipseOffsets0() const407     const Attribute* inEllipseOffsets0() const { return fInEllipseOffsets0; }
inEllipseOffsets1() const408     const Attribute* inEllipseOffsets1() const { return fInEllipseOffsets1; }
color() const409     GrColor color() const { return fColor; }
colorIgnored() const410     bool colorIgnored() const { return GrColor_ILLEGAL == fColor; }
viewMatrix() const411     const SkMatrix& viewMatrix() const { return fViewMatrix; }
usesLocalCoords() const412     bool usesLocalCoords() const { return fUsesLocalCoords; }
413 
getMode() const414     inline Mode getMode() const { return fMode; }
415 
416     class GLSLProcessor : public GrGLSLGeometryProcessor {
417     public:
GLSLProcessor()418         GLSLProcessor()
419             : fViewMatrix(SkMatrix::InvalidMatrix()) {}
420 
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)421         void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
422             const DIEllipseEdgeEffect& ee = args.fGP.cast<DIEllipseEdgeEffect>();
423             GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
424             GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
425             GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
426 
427             // emit attributes
428             varyingHandler->emitAttributes(ee);
429 
430             GrGLSLVertToFrag offsets0(kVec2f_GrSLType);
431             varyingHandler->addVarying("EllipseOffsets0", &offsets0);
432             vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(),
433                                    ee.inEllipseOffsets0()->fName);
434 
435             GrGLSLVertToFrag offsets1(kVec2f_GrSLType);
436             varyingHandler->addVarying("EllipseOffsets1", &offsets1);
437             vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(),
438                                    ee.inEllipseOffsets1()->fName);
439 
440             GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
441             // setup pass through color
442             if (!ee.colorIgnored()) {
443                 varyingHandler->addPassThroughAttribute(ee.inColor(), args.fOutputColor);
444             }
445 
446             // Setup position
447             this->setupPosition(vertBuilder,
448                                 uniformHandler,
449                                 gpArgs,
450                                 ee.inPosition()->fName,
451                                 ee.viewMatrix(),
452                                 &fViewMatrixUniform);
453 
454             // emit transforms
455             this->emitTransforms(vertBuilder,
456                                  varyingHandler,
457                                  uniformHandler,
458                                  gpArgs->fPositionVar,
459                                  ee.inPosition()->fName,
460                                  args.fTransformsIn,
461                                  args.fTransformsOut);
462 
463             SkAssertResult(fragBuilder->enableFeature(
464                     GrGLSLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
465             // for outer curve
466             fragBuilder->codeAppendf("vec2 scaledOffset = %s.xy;", offsets0.fsIn());
467             fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
468             fragBuilder->codeAppendf("vec2 duvdx = dFdx(%s);", offsets0.fsIn());
469             fragBuilder->codeAppendf("vec2 duvdy = dFdy(%s);", offsets0.fsIn());
470             fragBuilder->codeAppendf("vec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
471                                      "                 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
472                                      offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
473 
474             fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
475             // avoid calling inversesqrt on zero.
476             fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
477             fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
478             if (kHairline == ee.getMode()) {
479                 // can probably do this with one step
480                 fragBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
481                 fragBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
482             } else {
483                 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
484             }
485 
486             // for inner curve
487             if (kStroke == ee.getMode()) {
488                 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
489                 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
490                 fragBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
491                 fragBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
492                 fragBuilder->codeAppendf("grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
493                                          "            2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
494                                          offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(),
495                                          offsets1.fsIn());
496                 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
497                 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
498             }
499 
500             fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
501         }
502 
GenKey(const GrGeometryProcessor & gp,const GrGLSLCaps &,GrProcessorKeyBuilder * b)503         static void GenKey(const GrGeometryProcessor& gp,
504                            const GrGLSLCaps&,
505                            GrProcessorKeyBuilder* b) {
506             const DIEllipseEdgeEffect& ellipseEffect = gp.cast<DIEllipseEdgeEffect>();
507             uint16_t key = ellipseEffect.getMode();
508             key |= ellipseEffect.colorIgnored() << 9;
509             key |= ComputePosKey(ellipseEffect.viewMatrix()) << 10;
510             b->add32(key);
511         }
512 
setData(const GrGLSLProgramDataManager & pdman,const GrPrimitiveProcessor & gp)513         void setData(const GrGLSLProgramDataManager& pdman,
514                      const GrPrimitiveProcessor& gp) override {
515             const DIEllipseEdgeEffect& dee = gp.cast<DIEllipseEdgeEffect>();
516 
517             if (!dee.viewMatrix().isIdentity() && !fViewMatrix.cheapEqualTo(dee.viewMatrix())) {
518                 fViewMatrix = dee.viewMatrix();
519                 float viewMatrix[3 * 3];
520                 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
521                 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
522             }
523         }
524 
525     private:
526         SkMatrix fViewMatrix;
527         UniformHandle fViewMatrixUniform;
528 
529         typedef GrGLSLGeometryProcessor INHERITED;
530     };
531 
getGLSLProcessorKey(const GrGLSLCaps & caps,GrProcessorKeyBuilder * b) const532     void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
533         GLSLProcessor::GenKey(*this, caps, b);
534     }
535 
createGLSLInstance(const GrGLSLCaps &) const536     GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
537         return new GLSLProcessor();
538     }
539 
540 private:
DIEllipseEdgeEffect(GrColor color,const SkMatrix & viewMatrix,Mode mode,bool usesLocalCoords)541     DIEllipseEdgeEffect(GrColor color, const SkMatrix& viewMatrix, Mode mode,
542                         bool usesLocalCoords)
543         : fColor(color)
544         , fViewMatrix(viewMatrix)
545         , fUsesLocalCoords(usesLocalCoords) {
546         this->initClassID<DIEllipseEdgeEffect>();
547         fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType,
548                                                        kHigh_GrSLPrecision));
549         fInColor = &this->addVertexAttrib(Attribute("inColor", kVec4ub_GrVertexAttribType));
550         fInEllipseOffsets0 = &this->addVertexAttrib(Attribute("inEllipseOffsets0",
551                                                               kVec2f_GrVertexAttribType));
552         fInEllipseOffsets1 = &this->addVertexAttrib(Attribute("inEllipseOffsets1",
553                                                               kVec2f_GrVertexAttribType));
554         fMode = mode;
555     }
556 
557     const Attribute* fInPosition;
558     const Attribute* fInColor;
559     const Attribute* fInEllipseOffsets0;
560     const Attribute* fInEllipseOffsets1;
561     GrColor fColor;
562     SkMatrix fViewMatrix;
563     Mode fMode;
564     bool fUsesLocalCoords;
565 
566     GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
567 
568     typedef GrGeometryProcessor INHERITED;
569 };
570 
571 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseEdgeEffect);
572 
TestCreate(GrProcessorTestData * d)573 const GrGeometryProcessor* DIEllipseEdgeEffect::TestCreate(GrProcessorTestData* d) {
574     return DIEllipseEdgeEffect::Create(GrRandomColor(d->fRandom),
575                                        GrTest::TestMatrix(d->fRandom),
576                                        (Mode)(d->fRandom->nextRangeU(0,2)),
577                                        d->fRandom->nextBool());
578 }
579 
580 ///////////////////////////////////////////////////////////////////////////////
581 
CreateOvalBatch(GrColor color,const SkMatrix & viewMatrix,const SkRect & oval,const SkStrokeRec & stroke,GrShaderCaps * shaderCaps)582 GrDrawBatch* GrOvalRenderer::CreateOvalBatch(GrColor color,
583                                              const SkMatrix& viewMatrix,
584                                              const SkRect& oval,
585                                              const SkStrokeRec& stroke,
586                                              GrShaderCaps* shaderCaps) {
587     // we can draw circles
588     if (SkScalarNearlyEqual(oval.width(), oval.height()) && circle_stays_circle(viewMatrix)) {
589         return CreateCircleBatch(color, viewMatrix, oval, stroke);
590     }
591 
592     // if we have shader derivative support, render as device-independent
593     if (shaderCaps->shaderDerivativeSupport()) {
594         return CreateDIEllipseBatch(color, viewMatrix, oval, stroke);
595     }
596 
597     // otherwise axis-aligned ellipses only
598     if (viewMatrix.rectStaysRect()) {
599         return CreateEllipseBatch(color, viewMatrix, oval, stroke);
600     }
601 
602     return nullptr;
603 }
604 
605 ///////////////////////////////////////////////////////////////////////////////
606 
607 class CircleBatch : public GrVertexBatch {
608 public:
609     DEFINE_BATCH_CLASS_ID
610 
611     struct Geometry {
612         SkMatrix fViewMatrix;
613         SkRect fDevBounds;
614         SkScalar fInnerRadius;
615         SkScalar fOuterRadius;
616         GrColor fColor;
617         bool fStroke;
618     };
619 
Create(const Geometry & geometry)620     static GrDrawBatch* Create(const Geometry& geometry) { return new CircleBatch(geometry); }
621 
name() const622     const char* name() const override { return "CircleBatch"; }
623 
dumpInfo() const624     SkString dumpInfo() const override {
625         SkString string;
626         for (int i = 0; i < fGeoData.count(); ++i) {
627             string.appendf("Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
628                            "InnerRad: %.2f, OuterRad: %.2f\n",
629                            fGeoData[i].fColor,
630                            fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
631                            fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
632                            fGeoData[i].fInnerRadius,
633                            fGeoData[i].fOuterRadius);
634         }
635         string.append(INHERITED::dumpInfo());
636         return string;
637     }
638 
computePipelineOptimizations(GrInitInvariantOutput * color,GrInitInvariantOutput * coverage,GrBatchToXPOverrides * overrides) const639     void computePipelineOptimizations(GrInitInvariantOutput* color,
640                                       GrInitInvariantOutput* coverage,
641                                       GrBatchToXPOverrides* overrides) const override {
642         // When this is called on a batch, there is only one geometry bundle
643         color->setKnownFourComponents(fGeoData[0].fColor);
644         coverage->setUnknownSingleComponent();
645     }
646 
647 private:
initBatchTracker(const GrXPOverridesForBatch & overrides)648     void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
649         // Handle any color overrides
650         if (!overrides.readsColor()) {
651             fGeoData[0].fColor = GrColor_ILLEGAL;
652         }
653         overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
654 
655         // setup batch properties
656         fBatch.fColorIgnored = !overrides.readsColor();
657         fBatch.fColor = fGeoData[0].fColor;
658         fBatch.fStroke = fGeoData[0].fStroke;
659         fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
660         fBatch.fCoverageIgnored = !overrides.readsCoverage();
661     }
662 
onPrepareDraws(Target * target) const663     void onPrepareDraws(Target* target) const override {
664         SkMatrix invert;
665         if (!this->viewMatrix().invert(&invert)) {
666             return;
667         }
668 
669         // Setup geometry processor
670         SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(),
671                                                                       this->stroke(),
672                                                                       invert,
673                                                                       this->usesLocalCoords()));
674 
675         target->initDraw(gp, this->pipeline());
676 
677         int instanceCount = fGeoData.count();
678         size_t vertexStride = gp->getVertexStride();
679         SkASSERT(vertexStride == sizeof(CircleVertex));
680         QuadHelper helper;
681         CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(target, vertexStride,
682                                                                           instanceCount));
683         if (!verts) {
684             return;
685         }
686 
687         for (int i = 0; i < instanceCount; i++) {
688             const Geometry& geom = fGeoData[i];
689 
690             GrColor color = geom.fColor;
691             SkScalar innerRadius = geom.fInnerRadius;
692             SkScalar outerRadius = geom.fOuterRadius;
693 
694             const SkRect& bounds = geom.fDevBounds;
695 
696             // The inner radius in the vertex data must be specified in normalized space.
697             innerRadius = innerRadius / outerRadius;
698             verts[0].fPos = SkPoint::Make(bounds.fLeft,  bounds.fTop);
699             verts[0].fColor = color;
700             verts[0].fOffset = SkPoint::Make(-1, -1);
701             verts[0].fOuterRadius = outerRadius;
702             verts[0].fInnerRadius = innerRadius;
703 
704             verts[1].fPos = SkPoint::Make(bounds.fLeft,  bounds.fBottom);
705             verts[1].fColor = color;
706             verts[1].fOffset = SkPoint::Make(-1, 1);
707             verts[1].fOuterRadius = outerRadius;
708             verts[1].fInnerRadius = innerRadius;
709 
710             verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
711             verts[2].fColor = color;
712             verts[2].fOffset = SkPoint::Make(1, 1);
713             verts[2].fOuterRadius = outerRadius;
714             verts[2].fInnerRadius = innerRadius;
715 
716             verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
717             verts[3].fColor = color;
718             verts[3].fOffset = SkPoint::Make(1, -1);
719             verts[3].fOuterRadius = outerRadius;
720             verts[3].fInnerRadius = innerRadius;
721 
722             verts += kVerticesPerQuad;
723         }
724         helper.recordDraw(target);
725     }
726 
geoData()727     SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
728 
CircleBatch(const Geometry & geometry)729     CircleBatch(const Geometry& geometry) : INHERITED(ClassID()) {
730         fGeoData.push_back(geometry);
731 
732         this->setBounds(geometry.fDevBounds);
733     }
734 
onCombineIfPossible(GrBatch * t,const GrCaps & caps)735     bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
736         CircleBatch* that = t->cast<CircleBatch>();
737         if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
738                                     that->bounds(), caps)) {
739             return false;
740         }
741 
742         if (this->stroke() != that->stroke()) {
743             return false;
744         }
745 
746         SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
747         if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
748             return false;
749         }
750 
751         fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
752         this->joinBounds(that->bounds());
753         return true;
754     }
755 
color() const756     GrColor color() const { return fBatch.fColor; }
usesLocalCoords() const757     bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
viewMatrix() const758     const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
stroke() const759     bool stroke() const { return fBatch.fStroke; }
760 
761     struct BatchTracker {
762         GrColor fColor;
763         bool fStroke;
764         bool fUsesLocalCoords;
765         bool fColorIgnored;
766         bool fCoverageIgnored;
767     };
768 
769     BatchTracker fBatch;
770     SkSTArray<1, Geometry, true> fGeoData;
771 
772     typedef GrVertexBatch INHERITED;
773 };
774 
create_circle_batch(GrColor color,const SkMatrix & viewMatrix,const SkRect & circle,const SkStrokeRec & stroke)775 static GrDrawBatch* create_circle_batch(GrColor color,
776                                         const SkMatrix& viewMatrix,
777                                         const SkRect& circle,
778                                         const SkStrokeRec& stroke) {
779     SkPoint center = SkPoint::Make(circle.centerX(), circle.centerY());
780     viewMatrix.mapPoints(&center, 1);
781     SkScalar radius = viewMatrix.mapRadius(SkScalarHalf(circle.width()));
782     SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
783 
784     SkStrokeRec::Style style = stroke.getStyle();
785     bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
786                         SkStrokeRec::kHairline_Style == style;
787     bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
788 
789     SkScalar innerRadius = 0.0f;
790     SkScalar outerRadius = radius;
791     SkScalar halfWidth = 0;
792     if (hasStroke) {
793         if (SkScalarNearlyZero(strokeWidth)) {
794             halfWidth = SK_ScalarHalf;
795         } else {
796             halfWidth = SkScalarHalf(strokeWidth);
797         }
798 
799         outerRadius += halfWidth;
800         if (isStrokeOnly) {
801             innerRadius = radius - halfWidth;
802         }
803     }
804 
805     // The radii are outset for two reasons. First, it allows the shader to simply perform simpler
806     // computation because the computed alpha is zero, rather than 50%, at the radius.
807     // Second, the outer radius is used to compute the verts of the bounding box that is rendered
808     // and the outset ensures the box will cover all partially covered by the circle.
809     outerRadius += SK_ScalarHalf;
810     innerRadius -= SK_ScalarHalf;
811 
812     CircleBatch::Geometry geometry;
813     geometry.fViewMatrix = viewMatrix;
814     geometry.fColor = color;
815     geometry.fInnerRadius = innerRadius;
816     geometry.fOuterRadius = outerRadius;
817     geometry.fStroke = isStrokeOnly && innerRadius > 0;
818     geometry.fDevBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
819                                            center.fX + outerRadius, center.fY + outerRadius);
820 
821     return CircleBatch::Create(geometry);
822 }
823 
CreateCircleBatch(GrColor color,const SkMatrix & viewMatrix,const SkRect & circle,const SkStrokeRec & stroke)824 GrDrawBatch* GrOvalRenderer::CreateCircleBatch(GrColor color,
825                                                const SkMatrix& viewMatrix,
826                                                const SkRect& circle,
827                                                const SkStrokeRec& stroke) {
828     return create_circle_batch(color, viewMatrix, circle, stroke);
829 }
830 
831 ///////////////////////////////////////////////////////////////////////////////
832 
833 class EllipseBatch : public GrVertexBatch {
834 public:
835     DEFINE_BATCH_CLASS_ID
836 
837     struct Geometry {
838         SkMatrix fViewMatrix;
839         SkRect fDevBounds;
840         SkScalar fXRadius;
841         SkScalar fYRadius;
842         SkScalar fInnerXRadius;
843         SkScalar fInnerYRadius;
844         GrColor fColor;
845         bool fStroke;
846     };
847 
Create(const Geometry & geometry)848     static GrDrawBatch* Create(const Geometry& geometry) { return new EllipseBatch(geometry); }
849 
name() const850     const char* name() const override { return "EllipseBatch"; }
851 
computePipelineOptimizations(GrInitInvariantOutput * color,GrInitInvariantOutput * coverage,GrBatchToXPOverrides * overrides) const852     void computePipelineOptimizations(GrInitInvariantOutput* color,
853                                       GrInitInvariantOutput* coverage,
854                                       GrBatchToXPOverrides* overrides) const override {
855         // When this is called on a batch, there is only one geometry bundle
856         color->setKnownFourComponents(fGeoData[0].fColor);
857         coverage->setUnknownSingleComponent();
858     }
859 
860 private:
initBatchTracker(const GrXPOverridesForBatch & overrides)861     void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
862         // Handle any color overrides
863         if (!overrides.readsCoverage()) {
864             fGeoData[0].fColor = GrColor_ILLEGAL;
865         }
866         overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
867 
868         // setup batch properties
869         fBatch.fColorIgnored = !overrides.readsColor();
870         fBatch.fColor = fGeoData[0].fColor;
871         fBatch.fStroke = fGeoData[0].fStroke;
872         fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
873         fBatch.fCoverageIgnored = !overrides.readsCoverage();
874     }
875 
onPrepareDraws(Target * target) const876     void onPrepareDraws(Target* target) const override {
877         SkMatrix invert;
878         if (!this->viewMatrix().invert(&invert)) {
879             return;
880         }
881 
882         // Setup geometry processor
883         SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(),
884                                                                        this->stroke(),
885                                                                        invert,
886                                                                        this->usesLocalCoords()));
887 
888         target->initDraw(gp, this->pipeline());
889 
890         int instanceCount = fGeoData.count();
891         QuadHelper helper;
892         size_t vertexStride = gp->getVertexStride();
893         SkASSERT(vertexStride == sizeof(EllipseVertex));
894         EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
895             helper.init(target, vertexStride, instanceCount));
896         if (!verts) {
897             return;
898         }
899 
900         for (int i = 0; i < instanceCount; i++) {
901             const Geometry& geom = fGeoData[i];
902 
903             GrColor color = geom.fColor;
904             SkScalar xRadius = geom.fXRadius;
905             SkScalar yRadius = geom.fYRadius;
906 
907             // Compute the reciprocals of the radii here to save time in the shader
908             SkScalar xRadRecip = SkScalarInvert(xRadius);
909             SkScalar yRadRecip = SkScalarInvert(yRadius);
910             SkScalar xInnerRadRecip = SkScalarInvert(geom.fInnerXRadius);
911             SkScalar yInnerRadRecip = SkScalarInvert(geom.fInnerYRadius);
912 
913             const SkRect& bounds = geom.fDevBounds;
914 
915             // The inner radius in the vertex data must be specified in normalized space.
916             verts[0].fPos = SkPoint::Make(bounds.fLeft,  bounds.fTop);
917             verts[0].fColor = color;
918             verts[0].fOffset = SkPoint::Make(-xRadius, -yRadius);
919             verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
920             verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
921 
922             verts[1].fPos = SkPoint::Make(bounds.fLeft,  bounds.fBottom);
923             verts[1].fColor = color;
924             verts[1].fOffset = SkPoint::Make(-xRadius, yRadius);
925             verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
926             verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
927 
928             verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
929             verts[2].fColor = color;
930             verts[2].fOffset = SkPoint::Make(xRadius, yRadius);
931             verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
932             verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
933 
934             verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
935             verts[3].fColor = color;
936             verts[3].fOffset = SkPoint::Make(xRadius, -yRadius);
937             verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
938             verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
939 
940             verts += kVerticesPerQuad;
941         }
942         helper.recordDraw(target);
943     }
944 
geoData()945     SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
946 
EllipseBatch(const Geometry & geometry)947     EllipseBatch(const Geometry& geometry) : INHERITED(ClassID()) {
948         fGeoData.push_back(geometry);
949 
950         this->setBounds(geometry.fDevBounds);
951     }
952 
onCombineIfPossible(GrBatch * t,const GrCaps & caps)953     bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
954         EllipseBatch* that = t->cast<EllipseBatch>();
955 
956         if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
957                                     that->bounds(), caps)) {
958             return false;
959         }
960 
961         if (this->stroke() != that->stroke()) {
962             return false;
963         }
964 
965         SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
966         if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
967             return false;
968         }
969 
970         fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
971         this->joinBounds(that->bounds());
972         return true;
973     }
974 
color() const975     GrColor color() const { return fBatch.fColor; }
usesLocalCoords() const976     bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
viewMatrix() const977     const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
stroke() const978     bool stroke() const { return fBatch.fStroke; }
979 
980     struct BatchTracker {
981         GrColor fColor;
982         bool fStroke;
983         bool fUsesLocalCoords;
984         bool fColorIgnored;
985         bool fCoverageIgnored;
986     };
987 
988     BatchTracker fBatch;
989     SkSTArray<1, Geometry, true> fGeoData;
990 
991     typedef GrVertexBatch INHERITED;
992 };
993 
create_ellipse_batch(GrColor color,const SkMatrix & viewMatrix,const SkRect & ellipse,const SkStrokeRec & stroke)994 static GrDrawBatch* create_ellipse_batch(GrColor color,
995                                          const SkMatrix& viewMatrix,
996                                          const SkRect& ellipse,
997                                          const SkStrokeRec& stroke) {
998     SkASSERT(viewMatrix.rectStaysRect());
999 
1000     // do any matrix crunching before we reset the draw state for device coords
1001     SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1002     viewMatrix.mapPoints(&center, 1);
1003     SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1004     SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
1005     SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*ellipseXRadius +
1006                                    viewMatrix[SkMatrix::kMSkewY]*ellipseYRadius);
1007     SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*ellipseXRadius +
1008                                    viewMatrix[SkMatrix::kMScaleY]*ellipseYRadius);
1009 
1010     // do (potentially) anisotropic mapping of stroke
1011     SkVector scaledStroke;
1012     SkScalar strokeWidth = stroke.getWidth();
1013     scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1014                                                viewMatrix[SkMatrix::kMSkewY]));
1015     scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1016                                                viewMatrix[SkMatrix::kMScaleY]));
1017 
1018     SkStrokeRec::Style style = stroke.getStyle();
1019     bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1020                         SkStrokeRec::kHairline_Style == style;
1021     bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1022 
1023     SkScalar innerXRadius = 0;
1024     SkScalar innerYRadius = 0;
1025     if (hasStroke) {
1026         if (SkScalarNearlyZero(scaledStroke.length())) {
1027             scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1028         } else {
1029             scaledStroke.scale(SK_ScalarHalf);
1030         }
1031 
1032         // we only handle thick strokes for near-circular ellipses
1033         if (scaledStroke.length() > SK_ScalarHalf &&
1034             (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
1035             return nullptr;
1036         }
1037 
1038         // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1039         if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
1040             scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
1041             return nullptr;
1042         }
1043 
1044         // this is legit only if scale & translation (which should be the case at the moment)
1045         if (isStrokeOnly) {
1046             innerXRadius = xRadius - scaledStroke.fX;
1047             innerYRadius = yRadius - scaledStroke.fY;
1048         }
1049 
1050         xRadius += scaledStroke.fX;
1051         yRadius += scaledStroke.fY;
1052     }
1053 
1054     // We've extended the outer x radius out half a pixel to antialias.
1055     // This will also expand the rect so all the pixels will be captured.
1056     // TODO: Consider if we should use sqrt(2)/2 instead
1057     xRadius += SK_ScalarHalf;
1058     yRadius += SK_ScalarHalf;
1059 
1060     EllipseBatch::Geometry geometry;
1061     geometry.fViewMatrix = viewMatrix;
1062     geometry.fColor = color;
1063     geometry.fXRadius = xRadius;
1064     geometry.fYRadius = yRadius;
1065     geometry.fInnerXRadius = innerXRadius;
1066     geometry.fInnerYRadius = innerYRadius;
1067     geometry.fStroke = isStrokeOnly && innerXRadius > 0 && innerYRadius > 0;
1068     geometry.fDevBounds = SkRect::MakeLTRB(center.fX - xRadius, center.fY - yRadius,
1069                                            center.fX + xRadius, center.fY + yRadius);
1070 
1071     return EllipseBatch::Create(geometry);
1072 }
1073 
CreateEllipseBatch(GrColor color,const SkMatrix & viewMatrix,const SkRect & ellipse,const SkStrokeRec & stroke)1074 GrDrawBatch* GrOvalRenderer::CreateEllipseBatch(GrColor color,
1075                                                 const SkMatrix& viewMatrix,
1076                                                 const SkRect& ellipse,
1077                                                 const SkStrokeRec& stroke) {
1078     return create_ellipse_batch(color, viewMatrix, ellipse, stroke);
1079 }
1080 
1081 /////////////////////////////////////////////////////////////////////////////////////////////////
1082 
1083 class DIEllipseBatch : public GrVertexBatch {
1084 public:
1085     DEFINE_BATCH_CLASS_ID
1086 
1087     struct Geometry {
1088         SkMatrix fViewMatrix;
1089         SkRect fBounds;
1090         SkScalar fXRadius;
1091         SkScalar fYRadius;
1092         SkScalar fInnerXRadius;
1093         SkScalar fInnerYRadius;
1094         SkScalar fGeoDx;
1095         SkScalar fGeoDy;
1096         GrColor fColor;
1097         DIEllipseEdgeEffect::Mode fMode;
1098     };
1099 
Create(const Geometry & geometry,const SkRect & bounds)1100     static GrDrawBatch* Create(const Geometry& geometry, const SkRect& bounds) {
1101         return new DIEllipseBatch(geometry, bounds);
1102     }
1103 
name() const1104     const char* name() const override { return "DIEllipseBatch"; }
1105 
computePipelineOptimizations(GrInitInvariantOutput * color,GrInitInvariantOutput * coverage,GrBatchToXPOverrides * overrides) const1106     void computePipelineOptimizations(GrInitInvariantOutput* color,
1107                                       GrInitInvariantOutput* coverage,
1108                                       GrBatchToXPOverrides* overrides) const override {
1109         // When this is called on a batch, there is only one geometry bundle
1110         color->setKnownFourComponents(fGeoData[0].fColor);
1111         coverage->setUnknownSingleComponent();
1112     }
1113 
1114 private:
1115 
initBatchTracker(const GrXPOverridesForBatch & overrides)1116     void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
1117         // Handle any color overrides
1118         if (!overrides.readsColor()) {
1119             fGeoData[0].fColor = GrColor_ILLEGAL;
1120         }
1121         overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
1122 
1123         // setup batch properties
1124         fBatch.fColorIgnored = !overrides.readsColor();
1125         fBatch.fColor = fGeoData[0].fColor;
1126         fBatch.fMode = fGeoData[0].fMode;
1127         fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
1128         fBatch.fCoverageIgnored = !overrides.readsCoverage();
1129     }
1130 
onPrepareDraws(Target * target) const1131     void onPrepareDraws(Target* target) const override {
1132         // Setup geometry processor
1133         SkAutoTUnref<GrGeometryProcessor> gp(DIEllipseEdgeEffect::Create(this->color(),
1134                                                                          this->viewMatrix(),
1135                                                                          this->mode(),
1136                                                                          this->usesLocalCoords()));
1137 
1138         target->initDraw(gp, this->pipeline());
1139 
1140         int instanceCount = fGeoData.count();
1141         size_t vertexStride = gp->getVertexStride();
1142         SkASSERT(vertexStride == sizeof(DIEllipseVertex));
1143         QuadHelper helper;
1144         DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
1145             helper.init(target, vertexStride, instanceCount));
1146         if (!verts) {
1147             return;
1148         }
1149 
1150         for (int i = 0; i < instanceCount; i++) {
1151             const Geometry& geom = fGeoData[i];
1152 
1153             GrColor color = geom.fColor;
1154             SkScalar xRadius = geom.fXRadius;
1155             SkScalar yRadius = geom.fYRadius;
1156 
1157             const SkRect& bounds = geom.fBounds;
1158 
1159             // This adjusts the "radius" to include the half-pixel border
1160             SkScalar offsetDx = geom.fGeoDx / xRadius;
1161             SkScalar offsetDy = geom.fGeoDy / yRadius;
1162 
1163             SkScalar innerRatioX = xRadius / geom.fInnerXRadius;
1164             SkScalar innerRatioY = yRadius / geom.fInnerYRadius;
1165 
1166             verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
1167             verts[0].fColor = color;
1168             verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
1169             verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
1170 
1171             verts[1].fPos = SkPoint::Make(bounds.fLeft,  bounds.fBottom);
1172             verts[1].fColor = color;
1173             verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
1174             verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
1175 
1176             verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
1177             verts[2].fColor = color;
1178             verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1179             verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
1180 
1181             verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
1182             verts[3].fColor = color;
1183             verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1184             verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
1185 
1186             verts += kVerticesPerQuad;
1187         }
1188         helper.recordDraw(target);
1189     }
1190 
geoData()1191     SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1192 
DIEllipseBatch(const Geometry & geometry,const SkRect & bounds)1193     DIEllipseBatch(const Geometry& geometry, const SkRect& bounds) : INHERITED(ClassID()) {
1194         fGeoData.push_back(geometry);
1195 
1196         this->setBounds(bounds);
1197     }
1198 
onCombineIfPossible(GrBatch * t,const GrCaps & caps)1199     bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
1200         DIEllipseBatch* that = t->cast<DIEllipseBatch>();
1201         if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1202                                     that->bounds(), caps)) {
1203             return false;
1204         }
1205 
1206         if (this->mode() != that->mode()) {
1207             return false;
1208         }
1209 
1210         // TODO rewrite to allow positioning on CPU
1211         if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1212             return false;
1213         }
1214 
1215         fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
1216         this->joinBounds(that->bounds());
1217         return true;
1218     }
1219 
color() const1220     GrColor color() const { return fBatch.fColor; }
usesLocalCoords() const1221     bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
viewMatrix() const1222     const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
mode() const1223     DIEllipseEdgeEffect::Mode mode() const { return fBatch.fMode; }
1224 
1225     struct BatchTracker {
1226         GrColor fColor;
1227         DIEllipseEdgeEffect::Mode fMode;
1228         bool fUsesLocalCoords;
1229         bool fColorIgnored;
1230         bool fCoverageIgnored;
1231     };
1232 
1233     BatchTracker fBatch;
1234     SkSTArray<1, Geometry, true> fGeoData;
1235 
1236     typedef GrVertexBatch INHERITED;
1237 };
1238 
create_diellipse_batch(GrColor color,const SkMatrix & viewMatrix,const SkRect & ellipse,const SkStrokeRec & stroke)1239 static GrDrawBatch* create_diellipse_batch(GrColor color,
1240                                            const SkMatrix& viewMatrix,
1241                                            const SkRect& ellipse,
1242                                            const SkStrokeRec& stroke) {
1243     SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1244     SkScalar xRadius = SkScalarHalf(ellipse.width());
1245     SkScalar yRadius = SkScalarHalf(ellipse.height());
1246 
1247     SkStrokeRec::Style style = stroke.getStyle();
1248     DIEllipseEdgeEffect::Mode mode = (SkStrokeRec::kStroke_Style == style) ?
1249                                     DIEllipseEdgeEffect::kStroke :
1250                                     (SkStrokeRec::kHairline_Style == style) ?
1251                                     DIEllipseEdgeEffect::kHairline : DIEllipseEdgeEffect::kFill;
1252 
1253     SkScalar innerXRadius = 0;
1254     SkScalar innerYRadius = 0;
1255     if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
1256         SkScalar strokeWidth = stroke.getWidth();
1257 
1258         if (SkScalarNearlyZero(strokeWidth)) {
1259             strokeWidth = SK_ScalarHalf;
1260         } else {
1261             strokeWidth *= SK_ScalarHalf;
1262         }
1263 
1264         // we only handle thick strokes for near-circular ellipses
1265         if (strokeWidth > SK_ScalarHalf &&
1266             (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
1267             return nullptr;
1268         }
1269 
1270         // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1271         if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius ||
1272             strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) {
1273             return nullptr;
1274         }
1275 
1276         // set inner radius (if needed)
1277         if (SkStrokeRec::kStroke_Style == style) {
1278             innerXRadius = xRadius - strokeWidth;
1279             innerYRadius = yRadius - strokeWidth;
1280         }
1281 
1282         xRadius += strokeWidth;
1283         yRadius += strokeWidth;
1284     }
1285     if (DIEllipseEdgeEffect::kStroke == mode) {
1286         mode = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseEdgeEffect::kStroke :
1287                                                         DIEllipseEdgeEffect::kFill;
1288     }
1289 
1290     // This expands the outer rect so that after CTM we end up with a half-pixel border
1291     SkScalar a = viewMatrix[SkMatrix::kMScaleX];
1292     SkScalar b = viewMatrix[SkMatrix::kMSkewX];
1293     SkScalar c = viewMatrix[SkMatrix::kMSkewY];
1294     SkScalar d = viewMatrix[SkMatrix::kMScaleY];
1295     SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a*a + c*c);
1296     SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b*b + d*d);
1297 
1298     DIEllipseBatch::Geometry geometry;
1299     geometry.fViewMatrix = viewMatrix;
1300     geometry.fColor = color;
1301     geometry.fXRadius = xRadius;
1302     geometry.fYRadius = yRadius;
1303     geometry.fInnerXRadius = innerXRadius;
1304     geometry.fInnerYRadius = innerYRadius;
1305     geometry.fGeoDx = geoDx;
1306     geometry.fGeoDy = geoDy;
1307     geometry.fMode = mode;
1308     geometry.fBounds = SkRect::MakeLTRB(center.fX - xRadius - geoDx, center.fY - yRadius - geoDy,
1309                                         center.fX + xRadius + geoDx, center.fY + yRadius + geoDy);
1310 
1311     SkRect devBounds = geometry.fBounds;
1312     viewMatrix.mapRect(&devBounds);
1313     return DIEllipseBatch::Create(geometry, devBounds);
1314 }
1315 
CreateDIEllipseBatch(GrColor color,const SkMatrix & viewMatrix,const SkRect & ellipse,const SkStrokeRec & stroke)1316 GrDrawBatch* GrOvalRenderer::CreateDIEllipseBatch(GrColor color,
1317                                                   const SkMatrix& viewMatrix,
1318                                                   const SkRect& ellipse,
1319                                                   const SkStrokeRec& stroke) {
1320     return create_diellipse_batch(color, viewMatrix, ellipse, stroke);
1321 }
1322 
1323 ///////////////////////////////////////////////////////////////////////////////
1324 
1325 static const uint16_t gRRectIndices[] = {
1326     // corners
1327     0, 1, 5, 0, 5, 4,
1328     2, 3, 7, 2, 7, 6,
1329     8, 9, 13, 8, 13, 12,
1330     10, 11, 15, 10, 15, 14,
1331 
1332     // edges
1333     1, 2, 6, 1, 6, 5,
1334     4, 5, 9, 4, 9, 8,
1335     6, 7, 11, 6, 11, 10,
1336     9, 10, 14, 9, 14, 13,
1337 
1338     // center
1339     // we place this at the end so that we can ignore these indices when rendering stroke-only
1340     5, 6, 10, 5, 10, 9
1341 };
1342 
1343 static const int kIndicesPerStrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
1344 static const int kIndicesPerRRect = SK_ARRAY_COUNT(gRRectIndices);
1345 static const int kVertsPerRRect = 16;
1346 static const int kNumRRectsInIndexBuffer = 256;
1347 
1348 GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1349 GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
ref_rrect_index_buffer(bool strokeOnly,GrResourceProvider * resourceProvider)1350 static const GrIndexBuffer* ref_rrect_index_buffer(bool strokeOnly,
1351                                                    GrResourceProvider* resourceProvider) {
1352     GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1353     GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
1354     if (strokeOnly) {
1355         return resourceProvider->findOrCreateInstancedIndexBuffer(
1356             gRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
1357             gStrokeRRectOnlyIndexBufferKey);
1358     } else {
1359         return resourceProvider->findOrCreateInstancedIndexBuffer(
1360             gRRectIndices, kIndicesPerRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
1361             gRRectOnlyIndexBufferKey);
1362 
1363     }
1364 }
1365 
1366 ///////////////////////////////////////////////////////////////////////////////////////////////////
1367 
1368 class RRectCircleRendererBatch : public GrVertexBatch {
1369 public:
1370     DEFINE_BATCH_CLASS_ID
1371 
1372     struct Geometry {
1373         SkMatrix fViewMatrix;
1374         SkRect fDevBounds;
1375         SkScalar fInnerRadius;
1376         SkScalar fOuterRadius;
1377         GrColor fColor;
1378         bool fStroke;
1379     };
1380 
Create(const Geometry & geometry)1381     static GrDrawBatch* Create(const Geometry& geometry) {
1382         return new RRectCircleRendererBatch(geometry);
1383     }
1384 
name() const1385     const char* name() const override { return "RRectCircleBatch"; }
1386 
computePipelineOptimizations(GrInitInvariantOutput * color,GrInitInvariantOutput * coverage,GrBatchToXPOverrides * overrides) const1387     void computePipelineOptimizations(GrInitInvariantOutput* color,
1388                                       GrInitInvariantOutput* coverage,
1389                                       GrBatchToXPOverrides* overrides) const override {
1390         // When this is called on a batch, there is only one geometry bundle
1391         color->setKnownFourComponents(fGeoData[0].fColor);
1392         coverage->setUnknownSingleComponent();
1393     }
1394 
1395 private:
initBatchTracker(const GrXPOverridesForBatch & overrides)1396     void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
1397         // Handle any color overrides
1398         if (!overrides.readsColor()) {
1399             fGeoData[0].fColor = GrColor_ILLEGAL;
1400         }
1401         overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
1402 
1403         // setup batch properties
1404         fBatch.fColorIgnored = !overrides.readsColor();
1405         fBatch.fColor = fGeoData[0].fColor;
1406         fBatch.fStroke = fGeoData[0].fStroke;
1407         fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
1408         fBatch.fCoverageIgnored = !overrides.readsCoverage();
1409     }
1410 
onPrepareDraws(Target * target) const1411     void onPrepareDraws(Target* target) const override {
1412         // reset to device coordinates
1413         SkMatrix invert;
1414         if (!this->viewMatrix().invert(&invert)) {
1415             SkDebugf("Failed to invert\n");
1416             return;
1417         }
1418 
1419         // Setup geometry processor
1420         SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(),
1421                                                                       this->stroke(),
1422                                                                       invert,
1423                                                                       this->usesLocalCoords()));
1424 
1425         target->initDraw(gp, this->pipeline());
1426 
1427         int instanceCount = fGeoData.count();
1428         size_t vertexStride = gp->getVertexStride();
1429         SkASSERT(vertexStride == sizeof(CircleVertex));
1430 
1431         // drop out the middle quad if we're stroked
1432         int indicesPerInstance = this->stroke() ? kIndicesPerStrokeRRect : kIndicesPerRRect;
1433         SkAutoTUnref<const GrIndexBuffer> indexBuffer(
1434             ref_rrect_index_buffer(this->stroke(), target->resourceProvider()));
1435 
1436         InstancedHelper helper;
1437         CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(target,
1438             kTriangles_GrPrimitiveType, vertexStride, indexBuffer, kVertsPerRRect,
1439             indicesPerInstance, instanceCount));
1440         if (!verts || !indexBuffer) {
1441             SkDebugf("Could not allocate vertices\n");
1442             return;
1443         }
1444 
1445         for (int i = 0; i < instanceCount; i++) {
1446             const Geometry& args = fGeoData[i];
1447 
1448             GrColor color = args.fColor;
1449             SkScalar outerRadius = args.fOuterRadius;
1450 
1451             const SkRect& bounds = args.fDevBounds;
1452 
1453             SkScalar yCoords[4] = {
1454                 bounds.fTop,
1455                 bounds.fTop + outerRadius,
1456                 bounds.fBottom - outerRadius,
1457                 bounds.fBottom
1458             };
1459 
1460             SkScalar yOuterRadii[4] = {-1, 0, 0, 1 };
1461             // The inner radius in the vertex data must be specified in normalized space.
1462             SkScalar innerRadius = args.fInnerRadius / args.fOuterRadius;
1463             for (int i = 0; i < 4; ++i) {
1464                 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1465                 verts->fColor = color;
1466                 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1467                 verts->fOuterRadius = outerRadius;
1468                 verts->fInnerRadius = innerRadius;
1469                 verts++;
1470 
1471                 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
1472                 verts->fColor = color;
1473                 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1474                 verts->fOuterRadius = outerRadius;
1475                 verts->fInnerRadius = innerRadius;
1476                 verts++;
1477 
1478                 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
1479                 verts->fColor = color;
1480                 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1481                 verts->fOuterRadius = outerRadius;
1482                 verts->fInnerRadius = innerRadius;
1483                 verts++;
1484 
1485                 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1486                 verts->fColor = color;
1487                 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1488                 verts->fOuterRadius = outerRadius;
1489                 verts->fInnerRadius = innerRadius;
1490                 verts++;
1491             }
1492         }
1493 
1494         helper.recordDraw(target);
1495     }
1496 
geoData()1497     SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1498 
RRectCircleRendererBatch(const Geometry & geometry)1499     RRectCircleRendererBatch(const Geometry& geometry) : INHERITED(ClassID()) {
1500         fGeoData.push_back(geometry);
1501 
1502         this->setBounds(geometry.fDevBounds);
1503     }
1504 
onCombineIfPossible(GrBatch * t,const GrCaps & caps)1505     bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
1506         RRectCircleRendererBatch* that = t->cast<RRectCircleRendererBatch>();
1507         if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1508                                     that->bounds(), caps)) {
1509             return false;
1510         }
1511 
1512         if (this->stroke() != that->stroke()) {
1513             return false;
1514         }
1515 
1516         SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1517         if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1518             return false;
1519         }
1520 
1521         fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
1522         this->joinBounds(that->bounds());
1523         return true;
1524     }
1525 
color() const1526     GrColor color() const { return fBatch.fColor; }
usesLocalCoords() const1527     bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
viewMatrix() const1528     const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
stroke() const1529     bool stroke() const { return fBatch.fStroke; }
1530 
1531     struct BatchTracker {
1532         GrColor fColor;
1533         bool fStroke;
1534         bool fUsesLocalCoords;
1535         bool fColorIgnored;
1536         bool fCoverageIgnored;
1537     };
1538 
1539     BatchTracker fBatch;
1540     SkSTArray<1, Geometry, true> fGeoData;
1541 
1542     typedef GrVertexBatch INHERITED;
1543 };
1544 
1545 class RRectEllipseRendererBatch : public GrVertexBatch {
1546 public:
1547     DEFINE_BATCH_CLASS_ID
1548 
1549     struct Geometry {
1550         SkMatrix fViewMatrix;
1551         SkRect fDevBounds;
1552         SkScalar fXRadius;
1553         SkScalar fYRadius;
1554         SkScalar fInnerXRadius;
1555         SkScalar fInnerYRadius;
1556         GrColor fColor;
1557         bool fStroke;
1558     };
1559 
Create(const Geometry & geometry)1560     static GrDrawBatch* Create(const Geometry& geometry) {
1561         return new RRectEllipseRendererBatch(geometry);
1562     }
1563 
name() const1564     const char* name() const override { return "RRectEllipseRendererBatch"; }
1565 
computePipelineOptimizations(GrInitInvariantOutput * color,GrInitInvariantOutput * coverage,GrBatchToXPOverrides * overrides) const1566     void computePipelineOptimizations(GrInitInvariantOutput* color,
1567                                       GrInitInvariantOutput* coverage,
1568                                       GrBatchToXPOverrides* overrides) const override {
1569         // When this is called on a batch, there is only one geometry bundle
1570         color->setKnownFourComponents(fGeoData[0].fColor);
1571         coverage->setUnknownSingleComponent();
1572     }
1573 
1574 private:
initBatchTracker(const GrXPOverridesForBatch & overrides)1575     void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
1576         // Handle any color overrides
1577         if (!overrides.readsColor()) {
1578             fGeoData[0].fColor = GrColor_ILLEGAL;
1579         }
1580         overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
1581 
1582         // setup batch properties
1583         fBatch.fColorIgnored = !overrides.readsColor();
1584         fBatch.fColor = fGeoData[0].fColor;
1585         fBatch.fStroke = fGeoData[0].fStroke;
1586         fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
1587         fBatch.fCoverageIgnored = !overrides.readsCoverage();
1588     }
1589 
onPrepareDraws(Target * target) const1590     void onPrepareDraws(Target* target) const override {
1591         // reset to device coordinates
1592         SkMatrix invert;
1593         if (!this->viewMatrix().invert(&invert)) {
1594             SkDebugf("Failed to invert\n");
1595             return;
1596         }
1597 
1598         // Setup geometry processor
1599         SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(),
1600                                                                        this->stroke(),
1601                                                                        invert,
1602                                                                        this->usesLocalCoords()));
1603 
1604         target->initDraw(gp, this->pipeline());
1605 
1606         int instanceCount = fGeoData.count();
1607         size_t vertexStride = gp->getVertexStride();
1608         SkASSERT(vertexStride == sizeof(EllipseVertex));
1609 
1610         // drop out the middle quad if we're stroked
1611         int indicesPerInstance = this->stroke() ? kIndicesPerStrokeRRect : kIndicesPerRRect;
1612         SkAutoTUnref<const GrIndexBuffer> indexBuffer(
1613             ref_rrect_index_buffer(this->stroke(), target->resourceProvider()));
1614 
1615         InstancedHelper helper;
1616         EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
1617             helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer,
1618             kVertsPerRRect, indicesPerInstance, instanceCount));
1619         if (!verts || !indexBuffer) {
1620             SkDebugf("Could not allocate vertices\n");
1621             return;
1622         }
1623 
1624         for (int i = 0; i < instanceCount; i++) {
1625             const Geometry& args = fGeoData[i];
1626 
1627             GrColor color = args.fColor;
1628 
1629             // Compute the reciprocals of the radii here to save time in the shader
1630             SkScalar xRadRecip = SkScalarInvert(args.fXRadius);
1631             SkScalar yRadRecip = SkScalarInvert(args.fYRadius);
1632             SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
1633             SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
1634 
1635             // Extend the radii out half a pixel to antialias.
1636             SkScalar xOuterRadius = args.fXRadius + SK_ScalarHalf;
1637             SkScalar yOuterRadius = args.fYRadius + SK_ScalarHalf;
1638 
1639             const SkRect& bounds = args.fDevBounds;
1640 
1641             SkScalar yCoords[4] = {
1642                 bounds.fTop,
1643                 bounds.fTop + yOuterRadius,
1644                 bounds.fBottom - yOuterRadius,
1645                 bounds.fBottom
1646             };
1647             SkScalar yOuterOffsets[4] = {
1648                 yOuterRadius,
1649                 SK_ScalarNearlyZero, // we're using inversesqrt() in shader, so can't be exactly 0
1650                 SK_ScalarNearlyZero,
1651                 yOuterRadius
1652             };
1653 
1654             for (int i = 0; i < 4; ++i) {
1655                 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1656                 verts->fColor = color;
1657                 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1658                 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1659                 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1660                 verts++;
1661 
1662                 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
1663                 verts->fColor = color;
1664                 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1665                 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1666                 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1667                 verts++;
1668 
1669                 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
1670                 verts->fColor = color;
1671                 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1672                 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1673                 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1674                 verts++;
1675 
1676                 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1677                 verts->fColor = color;
1678                 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1679                 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1680                 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1681                 verts++;
1682             }
1683         }
1684         helper.recordDraw(target);
1685     }
1686 
geoData()1687     SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1688 
RRectEllipseRendererBatch(const Geometry & geometry)1689     RRectEllipseRendererBatch(const Geometry& geometry) : INHERITED(ClassID()) {
1690         fGeoData.push_back(geometry);
1691 
1692         this->setBounds(geometry.fDevBounds);
1693     }
1694 
onCombineIfPossible(GrBatch * t,const GrCaps & caps)1695     bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
1696         RRectEllipseRendererBatch* that = t->cast<RRectEllipseRendererBatch>();
1697 
1698         if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1699                                     that->bounds(), caps)) {
1700             return false;
1701         }
1702 
1703         if (this->stroke() != that->stroke()) {
1704             return false;
1705         }
1706 
1707         SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1708         if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1709             return false;
1710         }
1711 
1712         fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
1713         this->joinBounds(that->bounds());
1714         return true;
1715     }
1716 
color() const1717     GrColor color() const { return fBatch.fColor; }
usesLocalCoords() const1718     bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
viewMatrix() const1719     const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
stroke() const1720     bool stroke() const { return fBatch.fStroke; }
1721 
1722     struct BatchTracker {
1723         GrColor fColor;
1724         bool fStroke;
1725         bool fUsesLocalCoords;
1726         bool fColorIgnored;
1727         bool fCoverageIgnored;
1728     };
1729 
1730     BatchTracker fBatch;
1731     SkSTArray<1, Geometry, true> fGeoData;
1732 
1733     typedef GrVertexBatch INHERITED;
1734 };
1735 
create_rrect_batch(GrColor color,const SkMatrix & viewMatrix,const SkRRect & rrect,const SkStrokeRec & stroke)1736 static GrDrawBatch* create_rrect_batch(GrColor color,
1737                                        const SkMatrix& viewMatrix,
1738                                        const SkRRect& rrect,
1739                                        const SkStrokeRec& stroke) {
1740     SkASSERT(viewMatrix.rectStaysRect());
1741     SkASSERT(rrect.isSimple());
1742     SkASSERT(!rrect.isOval());
1743 
1744     // RRect batchs only handle simple, but not too simple, rrects
1745     // do any matrix crunching before we reset the draw state for device coords
1746     const SkRect& rrectBounds = rrect.getBounds();
1747     SkRect bounds;
1748     viewMatrix.mapRect(&bounds, rrectBounds);
1749 
1750     SkVector radii = rrect.getSimpleRadii();
1751     SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*radii.fX +
1752                                    viewMatrix[SkMatrix::kMSkewY]*radii.fY);
1753     SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*radii.fX +
1754                                    viewMatrix[SkMatrix::kMScaleY]*radii.fY);
1755 
1756     SkStrokeRec::Style style = stroke.getStyle();
1757 
1758     // do (potentially) anisotropic mapping of stroke
1759     SkVector scaledStroke;
1760     SkScalar strokeWidth = stroke.getWidth();
1761 
1762     bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1763                         SkStrokeRec::kHairline_Style == style;
1764     bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1765 
1766     if (hasStroke) {
1767         if (SkStrokeRec::kHairline_Style == style) {
1768             scaledStroke.set(1, 1);
1769         } else {
1770             scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1771                                                        viewMatrix[SkMatrix::kMSkewY]));
1772             scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1773                                                        viewMatrix[SkMatrix::kMScaleY]));
1774         }
1775 
1776         // if half of strokewidth is greater than radius, we don't handle that right now
1777         if (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius) {
1778             return nullptr;
1779         }
1780     }
1781 
1782     // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
1783     // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
1784     // patch will have fractional coverage. This only matters when the interior is actually filled.
1785     // We could consider falling back to rect rendering here, since a tiny radius is
1786     // indistinguishable from a square corner.
1787     if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
1788         return nullptr;
1789     }
1790 
1791     // if the corners are circles, use the circle renderer
1792     if ((!hasStroke || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) {
1793         SkScalar innerRadius = 0.0f;
1794         SkScalar outerRadius = xRadius;
1795         SkScalar halfWidth = 0;
1796         if (hasStroke) {
1797             if (SkScalarNearlyZero(scaledStroke.fX)) {
1798                 halfWidth = SK_ScalarHalf;
1799             } else {
1800                 halfWidth = SkScalarHalf(scaledStroke.fX);
1801             }
1802 
1803             if (isStrokeOnly) {
1804                 innerRadius = xRadius - halfWidth;
1805             }
1806             outerRadius += halfWidth;
1807             bounds.outset(halfWidth, halfWidth);
1808         }
1809 
1810         isStrokeOnly = (isStrokeOnly && innerRadius >= 0);
1811 
1812         // The radii are outset for two reasons. First, it allows the shader to simply perform
1813         // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1814         // Second, the outer radius is used to compute the verts of the bounding box that is
1815         // rendered and the outset ensures the box will cover all partially covered by the rrect
1816         // corners.
1817         outerRadius += SK_ScalarHalf;
1818         innerRadius -= SK_ScalarHalf;
1819 
1820         // Expand the rect so all the pixels will be captured.
1821         bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1822 
1823         RRectCircleRendererBatch::Geometry geometry;
1824         geometry.fViewMatrix = viewMatrix;
1825         geometry.fColor = color;
1826         geometry.fInnerRadius = innerRadius;
1827         geometry.fOuterRadius = outerRadius;
1828         geometry.fStroke = isStrokeOnly;
1829         geometry.fDevBounds = bounds;
1830 
1831         return RRectCircleRendererBatch::Create(geometry);
1832     // otherwise we use the ellipse renderer
1833     } else {
1834         SkScalar innerXRadius = 0.0f;
1835         SkScalar innerYRadius = 0.0f;
1836         if (hasStroke) {
1837             if (SkScalarNearlyZero(scaledStroke.length())) {
1838                 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1839             } else {
1840                 scaledStroke.scale(SK_ScalarHalf);
1841             }
1842 
1843             // we only handle thick strokes for near-circular ellipses
1844             if (scaledStroke.length() > SK_ScalarHalf &&
1845                 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
1846                 return nullptr;
1847             }
1848 
1849             // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1850             if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
1851                 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
1852                 return nullptr;
1853             }
1854 
1855             // this is legit only if scale & translation (which should be the case at the moment)
1856             if (isStrokeOnly) {
1857                 innerXRadius = xRadius - scaledStroke.fX;
1858                 innerYRadius = yRadius - scaledStroke.fY;
1859             }
1860 
1861             xRadius += scaledStroke.fX;
1862             yRadius += scaledStroke.fY;
1863             bounds.outset(scaledStroke.fX, scaledStroke.fY);
1864         }
1865 
1866         isStrokeOnly = (isStrokeOnly && innerXRadius >= 0 && innerYRadius >= 0);
1867 
1868         // Expand the rect so all the pixels will be captured.
1869         bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1870 
1871         RRectEllipseRendererBatch::Geometry geometry;
1872         geometry.fViewMatrix = viewMatrix;
1873         geometry.fColor = color;
1874         geometry.fXRadius = xRadius;
1875         geometry.fYRadius = yRadius;
1876         geometry.fInnerXRadius = innerXRadius;
1877         geometry.fInnerYRadius = innerYRadius;
1878         geometry.fStroke = isStrokeOnly;
1879         geometry.fDevBounds = bounds;
1880 
1881         return RRectEllipseRendererBatch::Create(geometry);
1882     }
1883 }
1884 
CreateRRectBatch(GrColor color,const SkMatrix & viewMatrix,const SkRRect & rrect,const SkStrokeRec & stroke,GrShaderCaps * shaderCaps)1885 GrDrawBatch* GrOvalRenderer::CreateRRectBatch(GrColor color,
1886                                               const SkMatrix& viewMatrix,
1887                                               const SkRRect& rrect,
1888                                               const SkStrokeRec& stroke,
1889                                               GrShaderCaps* shaderCaps) {
1890     if (rrect.isOval()) {
1891         return CreateOvalBatch(color, viewMatrix, rrect.getBounds(), stroke, shaderCaps);
1892     }
1893 
1894     if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
1895         return nullptr;
1896     }
1897 
1898     return create_rrect_batch(color, viewMatrix, rrect, stroke);
1899 }
1900 
1901 ///////////////////////////////////////////////////////////////////////////////////////////////////
1902 
1903 #ifdef GR_TEST_UTILS
1904 
DRAW_BATCH_TEST_DEFINE(CircleBatch)1905 DRAW_BATCH_TEST_DEFINE(CircleBatch) {
1906     SkMatrix viewMatrix = GrTest::TestMatrix(random);
1907     GrColor color = GrRandomColor(random);
1908     SkRect circle = GrTest::TestSquare(random);
1909     return create_circle_batch(color, viewMatrix, circle, GrTest::TestStrokeRec(random));
1910 }
1911 
DRAW_BATCH_TEST_DEFINE(EllipseBatch)1912 DRAW_BATCH_TEST_DEFINE(EllipseBatch) {
1913     SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
1914     GrColor color = GrRandomColor(random);
1915     SkRect ellipse = GrTest::TestSquare(random);
1916     return create_ellipse_batch(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
1917 }
1918 
DRAW_BATCH_TEST_DEFINE(DIEllipseBatch)1919 DRAW_BATCH_TEST_DEFINE(DIEllipseBatch) {
1920     SkMatrix viewMatrix = GrTest::TestMatrix(random);
1921     GrColor color = GrRandomColor(random);
1922     SkRect ellipse = GrTest::TestSquare(random);
1923     return create_diellipse_batch(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
1924 }
1925 
DRAW_BATCH_TEST_DEFINE(RRectBatch)1926 DRAW_BATCH_TEST_DEFINE(RRectBatch) {
1927     SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
1928     GrColor color = GrRandomColor(random);
1929     const SkRRect& rrect = GrTest::TestRRectSimple(random);
1930     return create_rrect_batch(color, viewMatrix, rrect, GrTest::TestStrokeRec(random));
1931 }
1932 
1933 #endif
1934