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