2 * Copyright 2013 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "GrOvalRenderer.h"
10 #include "GrBatchFlushState.h"
11 #include "GrBatchTest.h"
12 #include "GrDrawTarget.h"
13 #include "GrGeometryProcessor.h"
14 #include "GrInvariantOutput.h"
15 #include "GrPipelineBuilder.h"
16 #include "GrProcessor.h"
17 #include "GrResourceProvider.h"
18 #include "GrVertexBuffer.h"
20 #include "SkStrokeRec.h"
22 #include "batches/GrVertexBatch.h"
23 #include "effects/GrRRectEffect.h"
24 #include "glsl/GrGLSLFragmentShaderBuilder.h"
25 #include "glsl/GrGLSLGeometryProcessor.h"
26 #include "glsl/GrGLSLProgramDataManager.h"
27 #include "glsl/GrGLSLVarying.h"
28 #include "glsl/GrGLSLVertexShaderBuilder.h"
29 #include "glsl/GrGLSLUniformHandler.h"
30 #include "glsl/GrGLSLUtil.h"
32 // TODO(joshualitt) - Break this file up during GrBatch post implementation cleanup
35 // TODO(joshualitt) add per vertex colors
39 SkScalar fOuterRadius;
40 SkScalar fInnerRadius;
43 struct EllipseVertex {
50 struct DIEllipseVertex {
56 inline bool circle_stays_circle(const SkMatrix& m) {
57 return m.isSimilarity();
62 ///////////////////////////////////////////////////////////////////////////////
65 * The output of this effect is a modulation of the input color and coverage for a circle. It
66 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
67 * with origin at the circle center. Two vertex attributes are used:
68 * vec2f : position in device space of the bounding geometry vertices
69 * vec4f : (p.xy, outerRad, innerRad)
70 * p is the position in the normalized space.
71 * outerRad is the outerRadius in device space.
72 * innerRad is the innerRadius in normalized space (ignored if not stroking).
75 class CircleEdgeEffect : public GrGeometryProcessor {
77 static GrGeometryProcessor* Create(GrColor color, bool stroke, const SkMatrix& localMatrix,
78 bool usesLocalCoords) {
79 return new CircleEdgeEffect(color, stroke, localMatrix, usesLocalCoords);
82 const Attribute* inPosition() const { return fInPosition; }
83 const Attribute* inCircleEdge() const { return fInCircleEdge; }
84 GrColor color() const { return fColor; }
85 bool colorIgnored() const { return GrColor_ILLEGAL == fColor; }
86 const SkMatrix& localMatrix() const { return fLocalMatrix; }
87 bool usesLocalCoords() const { return fUsesLocalCoords; }
88 virtual ~CircleEdgeEffect() {}
90 const char* name() const override { return "CircleEdge"; }
92 inline bool isStroked() const { return fStroke; }
94 class GLSLProcessor : public GrGLSLGeometryProcessor {
97 : fColor(GrColor_ILLEGAL) {}
99 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
100 const CircleEdgeEffect& ce = args.fGP.cast<CircleEdgeEffect>();
101 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
102 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
103 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
106 varyingHandler->emitAttributes(ce);
108 GrGLSLVertToFrag v(kVec4f_GrSLType);
109 varyingHandler->addVarying("CircleEdge", &v);
110 vertBuilder->codeAppendf("%s = %s;", v.vsOut(), ce.inCircleEdge()->fName);
112 GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
113 // setup pass through color
114 if (!ce.colorIgnored()) {
115 this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor,
120 this->setupPosition(vertBuilder, gpArgs, ce.inPosition()->fName);
123 this->emitTransforms(vertBuilder,
126 gpArgs->fPositionVar,
127 ce.inPosition()->fName,
130 args.fTransformsOut);
132 fragBuilder->codeAppendf("float d = length(%s.xy);", v.fsIn());
133 fragBuilder->codeAppendf("float edgeAlpha = clamp(%s.z * (1.0 - d), 0.0, 1.0);",
135 if (ce.isStroked()) {
136 fragBuilder->codeAppendf("float innerAlpha = clamp(%s.z * (d - %s.w), 0.0, 1.0);",
138 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
141 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
144 static void GenKey(const GrGeometryProcessor& gp,
146 GrProcessorKeyBuilder* b) {
147 const CircleEdgeEffect& ce = gp.cast<CircleEdgeEffect>();
148 uint16_t key = ce.isStroked() ? 0x1 : 0x0;
149 key |= ce.usesLocalCoords() && ce.localMatrix().hasPerspective() ? 0x2 : 0x0;
150 key |= ce.colorIgnored() ? 0x4 : 0x0;
154 void setData(const GrGLSLProgramDataManager& pdman,
155 const GrPrimitiveProcessor& gp) override {
156 const CircleEdgeEffect& ce = gp.cast<CircleEdgeEffect>();
157 if (ce.color() != fColor) {
159 GrColorToRGBAFloat(ce.color(), c);
160 pdman.set4fv(fColorUniform, 1, c);
165 void setTransformData(const GrPrimitiveProcessor& primProc,
166 const GrGLSLProgramDataManager& pdman,
168 const SkTArray<const GrCoordTransform*, true>& transforms) override {
169 this->setTransformDataHelper<CircleEdgeEffect>(primProc, pdman, index, transforms);
174 UniformHandle fColorUniform;
175 typedef GrGLSLGeometryProcessor INHERITED;
178 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
179 GLSLProcessor::GenKey(*this, caps, b);
182 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
183 return new GLSLProcessor();
187 CircleEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix, bool usesLocalCoords)
189 , fLocalMatrix(localMatrix)
190 , fUsesLocalCoords(usesLocalCoords) {
191 this->initClassID<CircleEdgeEffect>();
192 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType,
193 kHigh_GrSLPrecision));
194 fInCircleEdge = &this->addVertexAttrib(Attribute("inCircleEdge",
195 kVec4f_GrVertexAttribType));
200 SkMatrix fLocalMatrix;
201 const Attribute* fInPosition;
202 const Attribute* fInCircleEdge;
204 bool fUsesLocalCoords;
206 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
208 typedef GrGeometryProcessor INHERITED;
211 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleEdgeEffect);
213 const GrGeometryProcessor* CircleEdgeEffect::TestCreate(GrProcessorTestData* d) {
214 return CircleEdgeEffect::Create(GrRandomColor(d->fRandom),
215 d->fRandom->nextBool(),
216 GrTest::TestMatrix(d->fRandom),
217 d->fRandom->nextBool());
220 ///////////////////////////////////////////////////////////////////////////////
223 * The output of this effect is a modulation of the input color and coverage for an axis-aligned
224 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
225 * in both x and y directions.
227 * We are using an implicit function of x^2/a^2 + y^2/b^2 - 1 = 0.
230 class EllipseEdgeEffect : public GrGeometryProcessor {
232 static GrGeometryProcessor* Create(GrColor color, bool stroke, const SkMatrix& localMatrix,
233 bool usesLocalCoords) {
234 return new EllipseEdgeEffect(color, stroke, localMatrix, usesLocalCoords);
237 virtual ~EllipseEdgeEffect() {}
239 const char* name() const override { return "EllipseEdge"; }
241 const Attribute* inPosition() const { return fInPosition; }
242 const Attribute* inEllipseOffset() const { return fInEllipseOffset; }
243 const Attribute* inEllipseRadii() const { return fInEllipseRadii; }
244 GrColor color() const { return fColor; }
245 bool colorIgnored() const { return GrColor_ILLEGAL == fColor; }
246 const SkMatrix& localMatrix() const { return fLocalMatrix; }
247 bool usesLocalCoords() const { return fUsesLocalCoords; }
249 inline bool isStroked() const { return fStroke; }
251 class GLSLProcessor : public GrGLSLGeometryProcessor {
254 : fColor(GrColor_ILLEGAL) {}
256 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
257 const EllipseEdgeEffect& ee = args.fGP.cast<EllipseEdgeEffect>();
258 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
259 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
260 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
263 varyingHandler->emitAttributes(ee);
265 GrGLSLVertToFrag ellipseOffsets(kVec2f_GrSLType);
266 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
267 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
268 ee.inEllipseOffset()->fName);
270 GrGLSLVertToFrag ellipseRadii(kVec4f_GrSLType);
271 varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
272 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(),
273 ee.inEllipseRadii()->fName);
275 GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
276 // setup pass through color
277 if (!ee.colorIgnored()) {
278 this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor,
283 this->setupPosition(vertBuilder, gpArgs, ee.inPosition()->fName);
286 this->emitTransforms(vertBuilder,
289 gpArgs->fPositionVar,
290 ee.inPosition()->fName,
293 args.fTransformsOut);
296 fragBuilder->codeAppendf("vec2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
297 ellipseRadii.fsIn());
298 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
299 fragBuilder->codeAppendf("vec2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
300 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
302 // avoid calling inversesqrt on zero.
303 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
304 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
305 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
308 if (ee.isStroked()) {
309 fragBuilder->codeAppendf("scaledOffset = %s*%s.zw;",
310 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
311 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
312 fragBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;",
313 ellipseRadii.fsIn());
314 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
315 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
318 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
321 static void GenKey(const GrGeometryProcessor& gp,
323 GrProcessorKeyBuilder* b) {
324 const EllipseEdgeEffect& ee = gp.cast<EllipseEdgeEffect>();
325 uint16_t key = ee.isStroked() ? 0x1 : 0x0;
326 key |= ee.usesLocalCoords() && ee.localMatrix().hasPerspective() ? 0x2 : 0x0;
327 key |= ee.colorIgnored() ? 0x4 : 0x0;
331 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp) override {
332 const EllipseEdgeEffect& ee = gp.cast<EllipseEdgeEffect>();
333 if (ee.color() != fColor) {
335 GrColorToRGBAFloat(ee.color(), c);
336 pdman.set4fv(fColorUniform, 1, c);
341 void setTransformData(const GrPrimitiveProcessor& primProc,
342 const GrGLSLProgramDataManager& pdman,
344 const SkTArray<const GrCoordTransform*, true>& transforms) override {
345 this->setTransformDataHelper<EllipseEdgeEffect>(primProc, pdman, index, transforms);
350 UniformHandle fColorUniform;
352 typedef GrGLSLGeometryProcessor INHERITED;
355 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
356 GLSLProcessor::GenKey(*this, caps, b);
359 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
360 return new GLSLProcessor();
364 EllipseEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix,
365 bool usesLocalCoords)
367 , fLocalMatrix(localMatrix)
368 , fUsesLocalCoords(usesLocalCoords) {
369 this->initClassID<EllipseEdgeEffect>();
370 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
371 fInEllipseOffset = &this->addVertexAttrib(Attribute("inEllipseOffset",
372 kVec2f_GrVertexAttribType));
373 fInEllipseRadii = &this->addVertexAttrib(Attribute("inEllipseRadii",
374 kVec4f_GrVertexAttribType));
378 const Attribute* fInPosition;
379 const Attribute* fInEllipseOffset;
380 const Attribute* fInEllipseRadii;
382 SkMatrix fLocalMatrix;
384 bool fUsesLocalCoords;
386 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
388 typedef GrGeometryProcessor INHERITED;
391 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseEdgeEffect);
393 const GrGeometryProcessor* EllipseEdgeEffect::TestCreate(GrProcessorTestData* d) {
394 return EllipseEdgeEffect::Create(GrRandomColor(d->fRandom),
395 d->fRandom->nextBool(),
396 GrTest::TestMatrix(d->fRandom),
397 d->fRandom->nextBool());
400 ///////////////////////////////////////////////////////////////////////////////
403 * The output of this effect is a modulation of the input color and coverage for an ellipse,
404 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
405 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
406 * using differentials.
408 * The result is device-independent and can be used with any affine matrix.
411 class DIEllipseEdgeEffect : public GrGeometryProcessor {
413 enum Mode { kStroke = 0, kHairline, kFill };
415 static GrGeometryProcessor* Create(GrColor color, const SkMatrix& viewMatrix, Mode mode,
416 bool usesLocalCoords) {
417 return new DIEllipseEdgeEffect(color, viewMatrix, mode, usesLocalCoords);
420 virtual ~DIEllipseEdgeEffect() {}
422 const char* name() const override { return "DIEllipseEdge"; }
424 const Attribute* inPosition() const { return fInPosition; }
425 const Attribute* inEllipseOffsets0() const { return fInEllipseOffsets0; }
426 const Attribute* inEllipseOffsets1() const { return fInEllipseOffsets1; }
427 GrColor color() const { return fColor; }
428 bool colorIgnored() const { return GrColor_ILLEGAL == fColor; }
429 const SkMatrix& viewMatrix() const { return fViewMatrix; }
430 bool usesLocalCoords() const { return fUsesLocalCoords; }
432 inline Mode getMode() const { return fMode; }
434 class GLSLProcessor : public GrGLSLGeometryProcessor {
437 : fViewMatrix(SkMatrix::InvalidMatrix()), fColor(GrColor_ILLEGAL) {}
439 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
440 const DIEllipseEdgeEffect& ee = args.fGP.cast<DIEllipseEdgeEffect>();
441 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
442 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
443 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
446 varyingHandler->emitAttributes(ee);
448 GrGLSLVertToFrag offsets0(kVec2f_GrSLType);
449 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
450 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(),
451 ee.inEllipseOffsets0()->fName);
453 GrGLSLVertToFrag offsets1(kVec2f_GrSLType);
454 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
455 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(),
456 ee.inEllipseOffsets1()->fName);
458 GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
459 // setup pass through color
460 if (!ee.colorIgnored()) {
461 this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor,
466 this->setupPosition(vertBuilder,
469 ee.inPosition()->fName,
471 &fViewMatrixUniform);
474 this->emitTransforms(vertBuilder,
477 gpArgs->fPositionVar,
478 ee.inPosition()->fName,
480 args.fTransformsOut);
482 SkAssertResult(fragBuilder->enableFeature(
483 GrGLSLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
485 fragBuilder->codeAppendf("vec2 scaledOffset = %s.xy;", offsets0.fsIn());
486 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
487 fragBuilder->codeAppendf("vec2 duvdx = dFdx(%s);", offsets0.fsIn());
488 fragBuilder->codeAppendf("vec2 duvdy = dFdy(%s);", offsets0.fsIn());
489 fragBuilder->codeAppendf("vec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
490 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
491 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
493 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
494 // avoid calling inversesqrt on zero.
495 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
496 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
497 if (kHairline == ee.getMode()) {
498 // can probably do this with one step
499 fragBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
500 fragBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
502 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
506 if (kStroke == ee.getMode()) {
507 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
508 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
509 fragBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
510 fragBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
511 fragBuilder->codeAppendf("grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
512 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
513 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(),
515 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
516 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
519 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
522 static void GenKey(const GrGeometryProcessor& gp,
524 GrProcessorKeyBuilder* b) {
525 const DIEllipseEdgeEffect& ellipseEffect = gp.cast<DIEllipseEdgeEffect>();
526 uint16_t key = ellipseEffect.getMode();
527 key |= ellipseEffect.colorIgnored() << 9;
528 key |= ComputePosKey(ellipseEffect.viewMatrix()) << 10;
532 void setData(const GrGLSLProgramDataManager& pdman,
533 const GrPrimitiveProcessor& gp) override {
534 const DIEllipseEdgeEffect& dee = gp.cast<DIEllipseEdgeEffect>();
536 if (!dee.viewMatrix().isIdentity() && !fViewMatrix.cheapEqualTo(dee.viewMatrix())) {
537 fViewMatrix = dee.viewMatrix();
538 float viewMatrix[3 * 3];
539 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
540 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
543 if (dee.color() != fColor) {
545 GrColorToRGBAFloat(dee.color(), c);
546 pdman.set4fv(fColorUniform, 1, c);
547 fColor = dee.color();
552 SkMatrix fViewMatrix;
554 UniformHandle fColorUniform;
555 UniformHandle fViewMatrixUniform;
557 typedef GrGLSLGeometryProcessor INHERITED;
560 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
561 GLSLProcessor::GenKey(*this, caps, b);
564 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
565 return new GLSLProcessor();
569 DIEllipseEdgeEffect(GrColor color, const SkMatrix& viewMatrix, Mode mode,
570 bool usesLocalCoords)
572 , fViewMatrix(viewMatrix)
573 , fUsesLocalCoords(usesLocalCoords) {
574 this->initClassID<DIEllipseEdgeEffect>();
575 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType,
576 kHigh_GrSLPrecision));
577 fInEllipseOffsets0 = &this->addVertexAttrib(Attribute("inEllipseOffsets0",
578 kVec2f_GrVertexAttribType));
579 fInEllipseOffsets1 = &this->addVertexAttrib(Attribute("inEllipseOffsets1",
580 kVec2f_GrVertexAttribType));
584 const Attribute* fInPosition;
585 const Attribute* fInEllipseOffsets0;
586 const Attribute* fInEllipseOffsets1;
588 SkMatrix fViewMatrix;
590 bool fUsesLocalCoords;
592 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
594 typedef GrGeometryProcessor INHERITED;
597 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseEdgeEffect);
599 const GrGeometryProcessor* DIEllipseEdgeEffect::TestCreate(GrProcessorTestData* d) {
600 return DIEllipseEdgeEffect::Create(GrRandomColor(d->fRandom),
601 GrTest::TestMatrix(d->fRandom),
602 (Mode)(d->fRandom->nextRangeU(0,2)),
603 d->fRandom->nextBool());
606 ///////////////////////////////////////////////////////////////////////////////
608 bool GrOvalRenderer::DrawOval(GrDrawTarget* target,
609 const GrPipelineBuilder& pipelineBuilder,
611 const SkMatrix& viewMatrix,
614 const SkStrokeRec& stroke) {
615 bool useCoverageAA = useAA && !pipelineBuilder.getRenderTarget()->isUnifiedMultisampled();
617 if (!useCoverageAA) {
621 // we can draw circles
622 if (SkScalarNearlyEqual(oval.width(), oval.height()) && circle_stays_circle(viewMatrix)) {
623 DrawCircle(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval, stroke);
624 // if we have shader derivative support, render as device-independent
625 } else if (target->caps()->shaderCaps()->shaderDerivativeSupport()) {
626 return DrawDIEllipse(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval,
628 // otherwise axis-aligned ellipses only
629 } else if (viewMatrix.rectStaysRect()) {
630 return DrawEllipse(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval,
639 ///////////////////////////////////////////////////////////////////////////////
641 class CircleBatch : public GrVertexBatch {
643 DEFINE_BATCH_CLASS_ID
646 SkMatrix fViewMatrix;
648 SkScalar fInnerRadius;
649 SkScalar fOuterRadius;
654 static GrDrawBatch* Create(const Geometry& geometry) { return new CircleBatch(geometry); }
656 const char* name() const override { return "CircleBatch"; }
658 SkString dumpInfo() const override {
660 for (int i = 0; i < fGeoData.count(); ++i) {
661 string.appendf("Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
662 "InnerRad: %.2f, OuterRad: %.2f\n",
664 fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
665 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
666 fGeoData[i].fInnerRadius,
667 fGeoData[i].fOuterRadius);
669 string.append(INHERITED::dumpInfo());
673 void computePipelineOptimizations(GrInitInvariantOutput* color,
674 GrInitInvariantOutput* coverage,
675 GrBatchToXPOverrides* overrides) const override {
676 // When this is called on a batch, there is only one geometry bundle
677 color->setKnownFourComponents(fGeoData[0].fColor);
678 coverage->setUnknownSingleComponent();
679 overrides->fUsePLSDstRead = false;
683 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
684 // Handle any color overrides
685 if (!overrides.readsColor()) {
686 fGeoData[0].fColor = GrColor_ILLEGAL;
688 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
690 // setup batch properties
691 fBatch.fColorIgnored = !overrides.readsColor();
692 fBatch.fColor = fGeoData[0].fColor;
693 fBatch.fStroke = fGeoData[0].fStroke;
694 fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
695 fBatch.fCoverageIgnored = !overrides.readsCoverage();
698 void onPrepareDraws(Target* target) const override {
700 if (!this->viewMatrix().invert(&invert)) {
704 // Setup geometry processor
705 SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(),
708 this->usesLocalCoords()));
710 target->initDraw(gp, this->pipeline());
712 int instanceCount = fGeoData.count();
713 size_t vertexStride = gp->getVertexStride();
714 SkASSERT(vertexStride == sizeof(CircleVertex));
716 CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(target, vertexStride,
722 for (int i = 0; i < instanceCount; i++) {
723 const Geometry& geom = fGeoData[i];
725 SkScalar innerRadius = geom.fInnerRadius;
726 SkScalar outerRadius = geom.fOuterRadius;
728 const SkRect& bounds = geom.fDevBounds;
730 // The inner radius in the vertex data must be specified in normalized space.
731 innerRadius = innerRadius / outerRadius;
732 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
733 verts[0].fOffset = SkPoint::Make(-1, -1);
734 verts[0].fOuterRadius = outerRadius;
735 verts[0].fInnerRadius = innerRadius;
737 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
738 verts[1].fOffset = SkPoint::Make(-1, 1);
739 verts[1].fOuterRadius = outerRadius;
740 verts[1].fInnerRadius = innerRadius;
742 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
743 verts[2].fOffset = SkPoint::Make(1, 1);
744 verts[2].fOuterRadius = outerRadius;
745 verts[2].fInnerRadius = innerRadius;
747 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
748 verts[3].fOffset = SkPoint::Make(1, -1);
749 verts[3].fOuterRadius = outerRadius;
750 verts[3].fInnerRadius = innerRadius;
752 verts += kVerticesPerQuad;
754 helper.recordDraw(target);
757 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
759 CircleBatch(const Geometry& geometry) : INHERITED(ClassID()) {
760 fGeoData.push_back(geometry);
762 this->setBounds(geometry.fDevBounds);
765 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
766 CircleBatch* that = t->cast<CircleBatch>();
767 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
768 that->bounds(), caps)) {
772 // TODO use vertex color to avoid breaking batches
773 if (this->color() != that->color()) {
777 if (this->stroke() != that->stroke()) {
781 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
782 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
786 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
787 this->joinBounds(that->bounds());
791 GrColor color() const { return fBatch.fColor; }
792 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
793 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
794 bool stroke() const { return fBatch.fStroke; }
796 struct BatchTracker {
799 bool fUsesLocalCoords;
801 bool fCoverageIgnored;
805 SkSTArray<1, Geometry, true> fGeoData;
807 typedef GrVertexBatch INHERITED;
810 static GrDrawBatch* create_circle_batch(GrColor color,
811 const SkMatrix& viewMatrix,
813 const SkRect& circle,
814 const SkStrokeRec& stroke) {
815 SkPoint center = SkPoint::Make(circle.centerX(), circle.centerY());
816 viewMatrix.mapPoints(¢er, 1);
817 SkScalar radius = viewMatrix.mapRadius(SkScalarHalf(circle.width()));
818 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
820 SkStrokeRec::Style style = stroke.getStyle();
821 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
822 SkStrokeRec::kHairline_Style == style;
823 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
825 SkScalar innerRadius = 0.0f;
826 SkScalar outerRadius = radius;
827 SkScalar halfWidth = 0;
829 if (SkScalarNearlyZero(strokeWidth)) {
830 halfWidth = SK_ScalarHalf;
832 halfWidth = SkScalarHalf(strokeWidth);
835 outerRadius += halfWidth;
837 innerRadius = radius - halfWidth;
841 // The radii are outset for two reasons. First, it allows the shader to simply perform simpler
842 // computation because the computed alpha is zero, rather than 50%, at the radius.
843 // Second, the outer radius is used to compute the verts of the bounding box that is rendered
844 // and the outset ensures the box will cover all partially covered by the circle.
845 outerRadius += SK_ScalarHalf;
846 innerRadius -= SK_ScalarHalf;
848 CircleBatch::Geometry geometry;
849 geometry.fViewMatrix = viewMatrix;
850 geometry.fColor = color;
851 geometry.fInnerRadius = innerRadius;
852 geometry.fOuterRadius = outerRadius;
853 geometry.fStroke = isStrokeOnly && innerRadius > 0;
854 geometry.fDevBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
855 center.fX + outerRadius, center.fY + outerRadius);
857 return CircleBatch::Create(geometry);
860 void GrOvalRenderer::DrawCircle(GrDrawTarget* target,
861 const GrPipelineBuilder& pipelineBuilder,
863 const SkMatrix& viewMatrix,
865 const SkRect& circle,
866 const SkStrokeRec& stroke) {
867 SkAutoTUnref<GrDrawBatch> batch(create_circle_batch(color, viewMatrix, useCoverageAA, circle,
869 target->drawBatch(pipelineBuilder, batch);
872 ///////////////////////////////////////////////////////////////////////////////
874 class EllipseBatch : public GrVertexBatch {
876 DEFINE_BATCH_CLASS_ID
879 SkMatrix fViewMatrix;
883 SkScalar fInnerXRadius;
884 SkScalar fInnerYRadius;
889 static GrDrawBatch* Create(const Geometry& geometry) { return new EllipseBatch(geometry); }
891 const char* name() const override { return "EllipseBatch"; }
893 void computePipelineOptimizations(GrInitInvariantOutput* color,
894 GrInitInvariantOutput* coverage,
895 GrBatchToXPOverrides* overrides) const override {
896 // When this is called on a batch, there is only one geometry bundle
897 color->setKnownFourComponents(fGeoData[0].fColor);
898 coverage->setUnknownSingleComponent();
899 overrides->fUsePLSDstRead = false;
903 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
904 // Handle any color overrides
905 if (!overrides.readsCoverage()) {
906 fGeoData[0].fColor = GrColor_ILLEGAL;
908 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
910 // setup batch properties
911 fBatch.fColorIgnored = !overrides.readsColor();
912 fBatch.fColor = fGeoData[0].fColor;
913 fBatch.fStroke = fGeoData[0].fStroke;
914 fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
915 fBatch.fCoverageIgnored = !overrides.readsCoverage();
918 void onPrepareDraws(Target* target) const override {
920 if (!this->viewMatrix().invert(&invert)) {
924 // Setup geometry processor
925 SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(),
928 this->usesLocalCoords()));
930 target->initDraw(gp, this->pipeline());
932 int instanceCount = fGeoData.count();
934 size_t vertexStride = gp->getVertexStride();
935 SkASSERT(vertexStride == sizeof(EllipseVertex));
936 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
937 helper.init(target, vertexStride, instanceCount));
942 for (int i = 0; i < instanceCount; i++) {
943 const Geometry& geom = fGeoData[i];
945 SkScalar xRadius = geom.fXRadius;
946 SkScalar yRadius = geom.fYRadius;
948 // Compute the reciprocals of the radii here to save time in the shader
949 SkScalar xRadRecip = SkScalarInvert(xRadius);
950 SkScalar yRadRecip = SkScalarInvert(yRadius);
951 SkScalar xInnerRadRecip = SkScalarInvert(geom.fInnerXRadius);
952 SkScalar yInnerRadRecip = SkScalarInvert(geom.fInnerYRadius);
954 const SkRect& bounds = geom.fDevBounds;
956 // The inner radius in the vertex data must be specified in normalized space.
957 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
958 verts[0].fOffset = SkPoint::Make(-xRadius, -yRadius);
959 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
960 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
962 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
963 verts[1].fOffset = SkPoint::Make(-xRadius, yRadius);
964 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
965 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
967 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
968 verts[2].fOffset = SkPoint::Make(xRadius, yRadius);
969 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
970 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
972 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
973 verts[3].fOffset = SkPoint::Make(xRadius, -yRadius);
974 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
975 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
977 verts += kVerticesPerQuad;
979 helper.recordDraw(target);
982 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
984 EllipseBatch(const Geometry& geometry) : INHERITED(ClassID()) {
985 fGeoData.push_back(geometry);
987 this->setBounds(geometry.fDevBounds);
990 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
991 EllipseBatch* that = t->cast<EllipseBatch>();
993 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
994 that->bounds(), caps)) {
998 // TODO use vertex color to avoid breaking batches
999 if (this->color() != that->color()) {
1003 if (this->stroke() != that->stroke()) {
1007 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1008 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1012 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
1013 this->joinBounds(that->bounds());
1017 GrColor color() const { return fBatch.fColor; }
1018 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1019 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1020 bool stroke() const { return fBatch.fStroke; }
1022 struct BatchTracker {
1025 bool fUsesLocalCoords;
1027 bool fCoverageIgnored;
1030 BatchTracker fBatch;
1031 SkSTArray<1, Geometry, true> fGeoData;
1033 typedef GrVertexBatch INHERITED;
1036 static GrDrawBatch* create_ellipse_batch(GrColor color,
1037 const SkMatrix& viewMatrix,
1039 const SkRect& ellipse,
1040 const SkStrokeRec& stroke) {
1043 // we should have checked for this previously
1044 bool isAxisAlignedEllipse = viewMatrix.rectStaysRect();
1045 SkASSERT(useCoverageAA && isAxisAlignedEllipse);
1049 // do any matrix crunching before we reset the draw state for device coords
1050 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1051 viewMatrix.mapPoints(¢er, 1);
1052 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1053 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
1054 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*ellipseXRadius +
1055 viewMatrix[SkMatrix::kMSkewY]*ellipseYRadius);
1056 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*ellipseXRadius +
1057 viewMatrix[SkMatrix::kMScaleY]*ellipseYRadius);
1059 // do (potentially) anisotropic mapping of stroke
1060 SkVector scaledStroke;
1061 SkScalar strokeWidth = stroke.getWidth();
1062 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1063 viewMatrix[SkMatrix::kMSkewY]));
1064 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1065 viewMatrix[SkMatrix::kMScaleY]));
1067 SkStrokeRec::Style style = stroke.getStyle();
1068 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1069 SkStrokeRec::kHairline_Style == style;
1070 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1072 SkScalar innerXRadius = 0;
1073 SkScalar innerYRadius = 0;
1075 if (SkScalarNearlyZero(scaledStroke.length())) {
1076 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1078 scaledStroke.scale(SK_ScalarHalf);
1081 // we only handle thick strokes for near-circular ellipses
1082 if (scaledStroke.length() > SK_ScalarHalf &&
1083 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
1087 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1088 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
1089 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
1093 // this is legit only if scale & translation (which should be the case at the moment)
1095 innerXRadius = xRadius - scaledStroke.fX;
1096 innerYRadius = yRadius - scaledStroke.fY;
1099 xRadius += scaledStroke.fX;
1100 yRadius += scaledStroke.fY;
1103 // We've extended the outer x radius out half a pixel to antialias.
1104 // This will also expand the rect so all the pixels will be captured.
1105 // TODO: Consider if we should use sqrt(2)/2 instead
1106 xRadius += SK_ScalarHalf;
1107 yRadius += SK_ScalarHalf;
1109 EllipseBatch::Geometry geometry;
1110 geometry.fViewMatrix = viewMatrix;
1111 geometry.fColor = color;
1112 geometry.fXRadius = xRadius;
1113 geometry.fYRadius = yRadius;
1114 geometry.fInnerXRadius = innerXRadius;
1115 geometry.fInnerYRadius = innerYRadius;
1116 geometry.fStroke = isStrokeOnly && innerXRadius > 0 && innerYRadius > 0;
1117 geometry.fDevBounds = SkRect::MakeLTRB(center.fX - xRadius, center.fY - yRadius,
1118 center.fX + xRadius, center.fY + yRadius);
1120 return EllipseBatch::Create(geometry);
1123 bool GrOvalRenderer::DrawEllipse(GrDrawTarget* target,
1124 const GrPipelineBuilder& pipelineBuilder,
1126 const SkMatrix& viewMatrix,
1128 const SkRect& ellipse,
1129 const SkStrokeRec& stroke) {
1130 SkAutoTUnref<GrDrawBatch> batch(create_ellipse_batch(color, viewMatrix, useCoverageAA, ellipse,
1136 target->drawBatch(pipelineBuilder, batch);
1140 /////////////////////////////////////////////////////////////////////////////////////////////////
1142 class DIEllipseBatch : public GrVertexBatch {
1144 DEFINE_BATCH_CLASS_ID
1147 SkMatrix fViewMatrix;
1151 SkScalar fInnerXRadius;
1152 SkScalar fInnerYRadius;
1156 DIEllipseEdgeEffect::Mode fMode;
1159 static GrDrawBatch* Create(const Geometry& geometry, const SkRect& bounds) {
1160 return new DIEllipseBatch(geometry, bounds);
1163 const char* name() const override { return "DIEllipseBatch"; }
1165 void computePipelineOptimizations(GrInitInvariantOutput* color,
1166 GrInitInvariantOutput* coverage,
1167 GrBatchToXPOverrides* overrides) const override {
1168 // When this is called on a batch, there is only one geometry bundle
1169 color->setKnownFourComponents(fGeoData[0].fColor);
1170 coverage->setUnknownSingleComponent();
1171 overrides->fUsePLSDstRead = false;
1176 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
1177 // Handle any color overrides
1178 if (!overrides.readsColor()) {
1179 fGeoData[0].fColor = GrColor_ILLEGAL;
1181 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
1183 // setup batch properties
1184 fBatch.fColorIgnored = !overrides.readsColor();
1185 fBatch.fColor = fGeoData[0].fColor;
1186 fBatch.fMode = fGeoData[0].fMode;
1187 fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
1188 fBatch.fCoverageIgnored = !overrides.readsCoverage();
1191 void onPrepareDraws(Target* target) const override {
1192 // Setup geometry processor
1193 SkAutoTUnref<GrGeometryProcessor> gp(DIEllipseEdgeEffect::Create(this->color(),
1196 this->usesLocalCoords()));
1198 target->initDraw(gp, this->pipeline());
1200 int instanceCount = fGeoData.count();
1201 size_t vertexStride = gp->getVertexStride();
1202 SkASSERT(vertexStride == sizeof(DIEllipseVertex));
1204 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
1205 helper.init(target, vertexStride, instanceCount));
1210 for (int i = 0; i < instanceCount; i++) {
1211 const Geometry& geom = fGeoData[i];
1213 SkScalar xRadius = geom.fXRadius;
1214 SkScalar yRadius = geom.fYRadius;
1216 const SkRect& bounds = geom.fBounds;
1218 // This adjusts the "radius" to include the half-pixel border
1219 SkScalar offsetDx = geom.fGeoDx / xRadius;
1220 SkScalar offsetDy = geom.fGeoDy / yRadius;
1222 SkScalar innerRatioX = xRadius / geom.fInnerXRadius;
1223 SkScalar innerRatioY = yRadius / geom.fInnerYRadius;
1225 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
1226 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
1227 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
1229 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
1230 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
1231 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
1233 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
1234 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1235 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
1237 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
1238 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1239 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
1241 verts += kVerticesPerQuad;
1243 helper.recordDraw(target);
1246 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1248 DIEllipseBatch(const Geometry& geometry, const SkRect& bounds) : INHERITED(ClassID()) {
1249 fGeoData.push_back(geometry);
1251 this->setBounds(bounds);
1254 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
1255 DIEllipseBatch* that = t->cast<DIEllipseBatch>();
1256 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1257 that->bounds(), caps)) {
1261 // TODO use vertex color to avoid breaking batches
1262 if (this->color() != that->color()) {
1266 if (this->mode() != that->mode()) {
1270 // TODO rewrite to allow positioning on CPU
1271 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1275 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
1276 this->joinBounds(that->bounds());
1280 GrColor color() const { return fBatch.fColor; }
1281 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1282 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1283 DIEllipseEdgeEffect::Mode mode() const { return fBatch.fMode; }
1285 struct BatchTracker {
1287 DIEllipseEdgeEffect::Mode fMode;
1288 bool fUsesLocalCoords;
1290 bool fCoverageIgnored;
1293 BatchTracker fBatch;
1294 SkSTArray<1, Geometry, true> fGeoData;
1296 typedef GrVertexBatch INHERITED;
1299 static GrDrawBatch* create_diellipse_batch(GrColor color,
1300 const SkMatrix& viewMatrix,
1302 const SkRect& ellipse,
1303 const SkStrokeRec& stroke) {
1304 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1305 SkScalar xRadius = SkScalarHalf(ellipse.width());
1306 SkScalar yRadius = SkScalarHalf(ellipse.height());
1308 SkStrokeRec::Style style = stroke.getStyle();
1309 DIEllipseEdgeEffect::Mode mode = (SkStrokeRec::kStroke_Style == style) ?
1310 DIEllipseEdgeEffect::kStroke :
1311 (SkStrokeRec::kHairline_Style == style) ?
1312 DIEllipseEdgeEffect::kHairline : DIEllipseEdgeEffect::kFill;
1314 SkScalar innerXRadius = 0;
1315 SkScalar innerYRadius = 0;
1316 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
1317 SkScalar strokeWidth = stroke.getWidth();
1319 if (SkScalarNearlyZero(strokeWidth)) {
1320 strokeWidth = SK_ScalarHalf;
1322 strokeWidth *= SK_ScalarHalf;
1325 // we only handle thick strokes for near-circular ellipses
1326 if (strokeWidth > SK_ScalarHalf &&
1327 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
1331 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1332 if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius ||
1333 strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) {
1337 // set inner radius (if needed)
1338 if (SkStrokeRec::kStroke_Style == style) {
1339 innerXRadius = xRadius - strokeWidth;
1340 innerYRadius = yRadius - strokeWidth;
1343 xRadius += strokeWidth;
1344 yRadius += strokeWidth;
1346 if (DIEllipseEdgeEffect::kStroke == mode) {
1347 mode = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseEdgeEffect::kStroke :
1348 DIEllipseEdgeEffect::kFill;
1351 // This expands the outer rect so that after CTM we end up with a half-pixel border
1352 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
1353 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
1354 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
1355 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
1356 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a*a + c*c);
1357 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b*b + d*d);
1359 DIEllipseBatch::Geometry geometry;
1360 geometry.fViewMatrix = viewMatrix;
1361 geometry.fColor = color;
1362 geometry.fXRadius = xRadius;
1363 geometry.fYRadius = yRadius;
1364 geometry.fInnerXRadius = innerXRadius;
1365 geometry.fInnerYRadius = innerYRadius;
1366 geometry.fGeoDx = geoDx;
1367 geometry.fGeoDy = geoDy;
1368 geometry.fMode = mode;
1369 geometry.fBounds = SkRect::MakeLTRB(center.fX - xRadius - geoDx, center.fY - yRadius - geoDy,
1370 center.fX + xRadius + geoDx, center.fY + yRadius + geoDy);
1372 SkRect devBounds = geometry.fBounds;
1373 viewMatrix.mapRect(&devBounds);
1374 return DIEllipseBatch::Create(geometry, devBounds);
1377 bool GrOvalRenderer::DrawDIEllipse(GrDrawTarget* target,
1378 const GrPipelineBuilder& pipelineBuilder,
1380 const SkMatrix& viewMatrix,
1382 const SkRect& ellipse,
1383 const SkStrokeRec& stroke) {
1384 SkAutoTUnref<GrDrawBatch> batch(create_diellipse_batch(color, viewMatrix, useCoverageAA,
1389 target->drawBatch(pipelineBuilder, batch);
1393 ///////////////////////////////////////////////////////////////////////////////
1395 static const uint16_t gRRectIndices[] = {
1399 8, 9, 13, 8, 13, 12,
1400 10, 11, 15, 10, 15, 14,
1405 6, 7, 11, 6, 11, 10,
1406 9, 10, 14, 9, 14, 13,
1409 // we place this at the end so that we can ignore these indices when rendering stroke-only
1413 static const int kIndicesPerStrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
1414 static const int kIndicesPerRRect = SK_ARRAY_COUNT(gRRectIndices);
1415 static const int kVertsPerRRect = 16;
1416 static const int kNumRRectsInIndexBuffer = 256;
1418 GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1419 GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
1420 static const GrIndexBuffer* ref_rrect_index_buffer(bool strokeOnly,
1421 GrResourceProvider* resourceProvider) {
1422 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1423 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
1425 return resourceProvider->findOrCreateInstancedIndexBuffer(
1426 gRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
1427 gStrokeRRectOnlyIndexBufferKey);
1429 return resourceProvider->findOrCreateInstancedIndexBuffer(
1430 gRRectIndices, kIndicesPerRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
1431 gRRectOnlyIndexBufferKey);
1436 bool GrOvalRenderer::DrawDRRect(GrDrawTarget* target,
1437 const GrPipelineBuilder& pipelineBuilder,
1439 const SkMatrix& viewMatrix,
1441 const SkRRect& origOuter,
1442 const SkRRect& origInner) {
1443 bool applyAA = useAA && !pipelineBuilder.getRenderTarget()->isUnifiedMultisampled();
1444 GrPipelineBuilder::AutoRestoreFragmentProcessorState arfps;
1445 if (!origInner.isEmpty()) {
1446 SkTCopyOnFirstWrite<SkRRect> inner(origInner);
1447 if (!viewMatrix.isIdentity()) {
1448 if (!origInner.transform(viewMatrix, inner.writable())) {
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 (nullptr == fp) {
1460 arfps.set(&pipelineBuilder);
1461 arfps.addCoverageFragmentProcessor(fp)->unref();
1464 SkStrokeRec fillRec(SkStrokeRec::kFill_InitStyle);
1465 if (DrawRRect(target, pipelineBuilder, color, viewMatrix, useAA, origOuter, fillRec)) {
1469 SkASSERT(!origOuter.isEmpty());
1470 SkTCopyOnFirstWrite<SkRRect> outer(origOuter);
1471 if (!viewMatrix.isIdentity()) {
1472 if (!origOuter.transform(viewMatrix, outer.writable())) {
1476 GrPrimitiveEdgeType edgeType = applyAA ? kFillAA_GrProcessorEdgeType :
1477 kFillBW_GrProcessorEdgeType;
1478 GrFragmentProcessor* effect = GrRRectEffect::Create(edgeType, *outer);
1479 if (nullptr == effect) {
1482 if (!arfps.isSet()) {
1483 arfps.set(&pipelineBuilder);
1487 if (!viewMatrix.invert(&invert)) {
1491 arfps.addCoverageFragmentProcessor(effect)->unref();
1492 SkRect bounds = outer->getBounds();
1494 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1496 target->drawNonAARect(pipelineBuilder, color, SkMatrix::I(), bounds, invert);
1500 ///////////////////////////////////////////////////////////////////////////////////////////////////
1502 class RRectCircleRendererBatch : public GrVertexBatch {
1504 DEFINE_BATCH_CLASS_ID
1507 SkMatrix fViewMatrix;
1509 SkScalar fInnerRadius;
1510 SkScalar fOuterRadius;
1515 static GrDrawBatch* Create(const Geometry& geometry) {
1516 return new RRectCircleRendererBatch(geometry);
1519 const char* name() const override { return "RRectCircleBatch"; }
1521 void computePipelineOptimizations(GrInitInvariantOutput* color,
1522 GrInitInvariantOutput* coverage,
1523 GrBatchToXPOverrides* overrides) const override {
1524 // When this is called on a batch, there is only one geometry bundle
1525 color->setKnownFourComponents(fGeoData[0].fColor);
1526 coverage->setUnknownSingleComponent();
1527 overrides->fUsePLSDstRead = false;
1531 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
1532 // Handle any color overrides
1533 if (!overrides.readsColor()) {
1534 fGeoData[0].fColor = GrColor_ILLEGAL;
1536 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
1538 // setup batch properties
1539 fBatch.fColorIgnored = !overrides.readsColor();
1540 fBatch.fColor = fGeoData[0].fColor;
1541 fBatch.fStroke = fGeoData[0].fStroke;
1542 fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
1543 fBatch.fCoverageIgnored = !overrides.readsCoverage();
1546 void onPrepareDraws(Target* target) const override {
1547 // reset to device coordinates
1549 if (!this->viewMatrix().invert(&invert)) {
1550 SkDebugf("Failed to invert\n");
1554 // Setup geometry processor
1555 SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(),
1558 this->usesLocalCoords()));
1560 target->initDraw(gp, this->pipeline());
1562 int instanceCount = fGeoData.count();
1563 size_t vertexStride = gp->getVertexStride();
1564 SkASSERT(vertexStride == sizeof(CircleVertex));
1566 // drop out the middle quad if we're stroked
1567 int indicesPerInstance = this->stroke() ? kIndicesPerStrokeRRect : kIndicesPerRRect;
1568 SkAutoTUnref<const GrIndexBuffer> indexBuffer(
1569 ref_rrect_index_buffer(this->stroke(), target->resourceProvider()));
1571 InstancedHelper helper;
1572 CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(target,
1573 kTriangles_GrPrimitiveType, vertexStride, indexBuffer, kVertsPerRRect,
1574 indicesPerInstance, instanceCount));
1575 if (!verts || !indexBuffer) {
1576 SkDebugf("Could not allocate vertices\n");
1580 for (int i = 0; i < instanceCount; i++) {
1581 const Geometry& args = fGeoData[i];
1583 SkScalar outerRadius = args.fOuterRadius;
1585 const SkRect& bounds = args.fDevBounds;
1587 SkScalar yCoords[4] = {
1589 bounds.fTop + outerRadius,
1590 bounds.fBottom - outerRadius,
1594 SkScalar yOuterRadii[4] = {-1, 0, 0, 1 };
1595 // The inner radius in the vertex data must be specified in normalized space.
1596 SkScalar innerRadius = args.fInnerRadius / args.fOuterRadius;
1597 for (int i = 0; i < 4; ++i) {
1598 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1599 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1600 verts->fOuterRadius = outerRadius;
1601 verts->fInnerRadius = innerRadius;
1604 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
1605 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1606 verts->fOuterRadius = outerRadius;
1607 verts->fInnerRadius = innerRadius;
1610 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
1611 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1612 verts->fOuterRadius = outerRadius;
1613 verts->fInnerRadius = innerRadius;
1616 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1617 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1618 verts->fOuterRadius = outerRadius;
1619 verts->fInnerRadius = innerRadius;
1624 helper.recordDraw(target);
1627 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1629 RRectCircleRendererBatch(const Geometry& geometry) : INHERITED(ClassID()) {
1630 fGeoData.push_back(geometry);
1632 this->setBounds(geometry.fDevBounds);
1635 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
1636 RRectCircleRendererBatch* that = t->cast<RRectCircleRendererBatch>();
1637 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1638 that->bounds(), caps)) {
1642 // TODO use vertex color to avoid breaking batches
1643 if (this->color() != that->color()) {
1647 if (this->stroke() != that->stroke()) {
1651 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1652 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1656 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
1657 this->joinBounds(that->bounds());
1661 GrColor color() const { return fBatch.fColor; }
1662 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1663 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1664 bool stroke() const { return fBatch.fStroke; }
1666 struct BatchTracker {
1669 bool fUsesLocalCoords;
1671 bool fCoverageIgnored;
1674 BatchTracker fBatch;
1675 SkSTArray<1, Geometry, true> fGeoData;
1677 typedef GrVertexBatch INHERITED;
1680 class RRectEllipseRendererBatch : public GrVertexBatch {
1682 DEFINE_BATCH_CLASS_ID
1685 SkMatrix fViewMatrix;
1689 SkScalar fInnerXRadius;
1690 SkScalar fInnerYRadius;
1695 static GrDrawBatch* Create(const Geometry& geometry) {
1696 return new RRectEllipseRendererBatch(geometry);
1699 const char* name() const override { return "RRectEllipseRendererBatch"; }
1701 void computePipelineOptimizations(GrInitInvariantOutput* color,
1702 GrInitInvariantOutput* coverage,
1703 GrBatchToXPOverrides* overrides) const override {
1704 // When this is called on a batch, there is only one geometry bundle
1705 color->setKnownFourComponents(fGeoData[0].fColor);
1706 coverage->setUnknownSingleComponent();
1707 overrides->fUsePLSDstRead = false;
1711 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
1712 // Handle any color overrides
1713 if (!overrides.readsColor()) {
1714 fGeoData[0].fColor = GrColor_ILLEGAL;
1716 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
1718 // setup batch properties
1719 fBatch.fColorIgnored = !overrides.readsColor();
1720 fBatch.fColor = fGeoData[0].fColor;
1721 fBatch.fStroke = fGeoData[0].fStroke;
1722 fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
1723 fBatch.fCoverageIgnored = !overrides.readsCoverage();
1726 void onPrepareDraws(Target* target) const override {
1727 // reset to device coordinates
1729 if (!this->viewMatrix().invert(&invert)) {
1730 SkDebugf("Failed to invert\n");
1734 // Setup geometry processor
1735 SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(),
1738 this->usesLocalCoords()));
1740 target->initDraw(gp, this->pipeline());
1742 int instanceCount = fGeoData.count();
1743 size_t vertexStride = gp->getVertexStride();
1744 SkASSERT(vertexStride == sizeof(EllipseVertex));
1746 // drop out the middle quad if we're stroked
1747 int indicesPerInstance = this->stroke() ? kIndicesPerStrokeRRect : kIndicesPerRRect;
1748 SkAutoTUnref<const GrIndexBuffer> indexBuffer(
1749 ref_rrect_index_buffer(this->stroke(), target->resourceProvider()));
1751 InstancedHelper helper;
1752 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
1753 helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer,
1754 kVertsPerRRect, indicesPerInstance, instanceCount));
1755 if (!verts || !indexBuffer) {
1756 SkDebugf("Could not allocate vertices\n");
1760 for (int i = 0; i < instanceCount; i++) {
1761 const Geometry& args = fGeoData[i];
1763 // Compute the reciprocals of the radii here to save time in the shader
1764 SkScalar xRadRecip = SkScalarInvert(args.fXRadius);
1765 SkScalar yRadRecip = SkScalarInvert(args.fYRadius);
1766 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
1767 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
1769 // Extend the radii out half a pixel to antialias.
1770 SkScalar xOuterRadius = args.fXRadius + SK_ScalarHalf;
1771 SkScalar yOuterRadius = args.fYRadius + SK_ScalarHalf;
1773 const SkRect& bounds = args.fDevBounds;
1775 SkScalar yCoords[4] = {
1777 bounds.fTop + yOuterRadius,
1778 bounds.fBottom - yOuterRadius,
1781 SkScalar yOuterOffsets[4] = {
1783 SK_ScalarNearlyZero, // we're using inversesqrt() in shader, so can't be exactly 0
1784 SK_ScalarNearlyZero,
1788 for (int i = 0; i < 4; ++i) {
1789 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1790 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1791 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1792 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1795 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
1796 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1797 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1798 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1801 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
1802 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1803 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1804 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1807 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1808 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1809 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1810 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1814 helper.recordDraw(target);
1817 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1819 RRectEllipseRendererBatch(const Geometry& geometry) : INHERITED(ClassID()) {
1820 fGeoData.push_back(geometry);
1822 this->setBounds(geometry.fDevBounds);
1825 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
1826 RRectEllipseRendererBatch* that = t->cast<RRectEllipseRendererBatch>();
1828 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1829 that->bounds(), caps)) {
1833 // TODO use vertex color to avoid breaking batches
1834 if (this->color() != that->color()) {
1838 if (this->stroke() != that->stroke()) {
1842 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1843 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1847 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
1848 this->joinBounds(that->bounds());
1852 GrColor color() const { return fBatch.fColor; }
1853 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1854 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1855 bool stroke() const { return fBatch.fStroke; }
1857 struct BatchTracker {
1860 bool fUsesLocalCoords;
1862 bool fCoverageIgnored;
1865 BatchTracker fBatch;
1866 SkSTArray<1, Geometry, true> fGeoData;
1868 typedef GrVertexBatch INHERITED;
1871 static GrDrawBatch* create_rrect_batch(GrColor color,
1872 const SkMatrix& viewMatrix,
1873 const SkRRect& rrect,
1874 const SkStrokeRec& stroke) {
1875 SkASSERT(viewMatrix.rectStaysRect());
1876 SkASSERT(rrect.isSimple());
1877 SkASSERT(!rrect.isOval());
1879 // RRect batchs only handle simple, but not too simple, rrects
1880 // do any matrix crunching before we reset the draw state for device coords
1881 const SkRect& rrectBounds = rrect.getBounds();
1883 viewMatrix.mapRect(&bounds, rrectBounds);
1885 SkVector radii = rrect.getSimpleRadii();
1886 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*radii.fX +
1887 viewMatrix[SkMatrix::kMSkewY]*radii.fY);
1888 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*radii.fX +
1889 viewMatrix[SkMatrix::kMScaleY]*radii.fY);
1891 SkStrokeRec::Style style = stroke.getStyle();
1893 // do (potentially) anisotropic mapping of stroke
1894 SkVector scaledStroke;
1895 SkScalar strokeWidth = stroke.getWidth();
1897 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1898 SkStrokeRec::kHairline_Style == style;
1899 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1902 if (SkStrokeRec::kHairline_Style == style) {
1903 scaledStroke.set(1, 1);
1905 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1906 viewMatrix[SkMatrix::kMSkewY]));
1907 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1908 viewMatrix[SkMatrix::kMScaleY]));
1911 // if half of strokewidth is greater than radius, we don't handle that right now
1912 if (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius) {
1917 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
1918 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
1919 // patch will have fractional coverage. This only matters when the interior is actually filled.
1920 // We could consider falling back to rect rendering here, since a tiny radius is
1921 // indistinguishable from a square corner.
1922 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
1926 // if the corners are circles, use the circle renderer
1927 if ((!hasStroke || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) {
1928 SkScalar innerRadius = 0.0f;
1929 SkScalar outerRadius = xRadius;
1930 SkScalar halfWidth = 0;
1932 if (SkScalarNearlyZero(scaledStroke.fX)) {
1933 halfWidth = SK_ScalarHalf;
1935 halfWidth = SkScalarHalf(scaledStroke.fX);
1939 innerRadius = xRadius - halfWidth;
1941 outerRadius += halfWidth;
1942 bounds.outset(halfWidth, halfWidth);
1945 isStrokeOnly = (isStrokeOnly && innerRadius >= 0);
1947 // The radii are outset for two reasons. First, it allows the shader to simply perform
1948 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1949 // Second, the outer radius is used to compute the verts of the bounding box that is
1950 // rendered and the outset ensures the box will cover all partially covered by the rrect
1952 outerRadius += SK_ScalarHalf;
1953 innerRadius -= SK_ScalarHalf;
1955 // Expand the rect so all the pixels will be captured.
1956 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1958 RRectCircleRendererBatch::Geometry geometry;
1959 geometry.fViewMatrix = viewMatrix;
1960 geometry.fColor = color;
1961 geometry.fInnerRadius = innerRadius;
1962 geometry.fOuterRadius = outerRadius;
1963 geometry.fStroke = isStrokeOnly;
1964 geometry.fDevBounds = bounds;
1966 return RRectCircleRendererBatch::Create(geometry);
1967 // otherwise we use the ellipse renderer
1969 SkScalar innerXRadius = 0.0f;
1970 SkScalar innerYRadius = 0.0f;
1972 if (SkScalarNearlyZero(scaledStroke.length())) {
1973 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1975 scaledStroke.scale(SK_ScalarHalf);
1978 // we only handle thick strokes for near-circular ellipses
1979 if (scaledStroke.length() > SK_ScalarHalf &&
1980 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
1984 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1985 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
1986 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
1990 // this is legit only if scale & translation (which should be the case at the moment)
1992 innerXRadius = xRadius - scaledStroke.fX;
1993 innerYRadius = yRadius - scaledStroke.fY;
1996 xRadius += scaledStroke.fX;
1997 yRadius += scaledStroke.fY;
1998 bounds.outset(scaledStroke.fX, scaledStroke.fY);
2001 isStrokeOnly = (isStrokeOnly && innerXRadius >= 0 && innerYRadius >= 0);
2003 // Expand the rect so all the pixels will be captured.
2004 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
2006 RRectEllipseRendererBatch::Geometry geometry;
2007 geometry.fViewMatrix = viewMatrix;
2008 geometry.fColor = color;
2009 geometry.fXRadius = xRadius;
2010 geometry.fYRadius = yRadius;
2011 geometry.fInnerXRadius = innerXRadius;
2012 geometry.fInnerYRadius = innerYRadius;
2013 geometry.fStroke = isStrokeOnly;
2014 geometry.fDevBounds = bounds;
2016 return RRectEllipseRendererBatch::Create(geometry);
2020 bool GrOvalRenderer::DrawRRect(GrDrawTarget* target,
2021 const GrPipelineBuilder& pipelineBuilder,
2023 const SkMatrix& viewMatrix,
2025 const SkRRect& rrect,
2026 const SkStrokeRec& stroke) {
2027 if (rrect.isOval()) {
2028 return DrawOval(target, pipelineBuilder, color, viewMatrix, useAA, rrect.getBounds(),
2032 bool useCoverageAA = useAA && !pipelineBuilder.getRenderTarget()->isUnifiedMultisampled();
2034 // only anti-aliased rrects for now
2035 if (!useCoverageAA) {
2039 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
2043 SkAutoTUnref<GrDrawBatch> batch(create_rrect_batch(color, viewMatrix, rrect, stroke));
2048 target->drawBatch(pipelineBuilder, batch);
2052 ///////////////////////////////////////////////////////////////////////////////////////////////////
2054 #ifdef GR_TEST_UTILS
2056 DRAW_BATCH_TEST_DEFINE(CircleBatch) {
2057 SkMatrix viewMatrix = GrTest::TestMatrix(random);
2058 GrColor color = GrRandomColor(random);
2059 bool useCoverageAA = random->nextBool();
2060 SkRect circle = GrTest::TestSquare(random);
2061 return create_circle_batch(color, viewMatrix, useCoverageAA, circle,
2062 GrTest::TestStrokeRec(random));
2065 DRAW_BATCH_TEST_DEFINE(EllipseBatch) {
2066 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2067 GrColor color = GrRandomColor(random);
2068 SkRect ellipse = GrTest::TestSquare(random);
2069 return create_ellipse_batch(color, viewMatrix, true, ellipse,
2070 GrTest::TestStrokeRec(random));
2073 DRAW_BATCH_TEST_DEFINE(DIEllipseBatch) {
2074 SkMatrix viewMatrix = GrTest::TestMatrix(random);
2075 GrColor color = GrRandomColor(random);
2076 bool useCoverageAA = random->nextBool();
2077 SkRect ellipse = GrTest::TestSquare(random);
2078 return create_diellipse_batch(color, viewMatrix, useCoverageAA, ellipse,
2079 GrTest::TestStrokeRec(random));
2082 DRAW_BATCH_TEST_DEFINE(RRectBatch) {
2083 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2084 GrColor color = GrRandomColor(random);
2085 const SkRRect& rrect = GrTest::TestRRectSimple(random);
2086 return create_rrect_batch(color, viewMatrix, rrect, GrTest::TestStrokeRec(random));