blob: eb22ca9cb41d795c439aaea5232dfac4703faf7d [file] [log] [blame]
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001/*
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
joshualitt76e7fb62015-02-11 08:52:27 -080010#include "GrBatch.h"
11#include "GrBatchTarget.h"
joshualitt3e708c52015-04-30 13:49:27 -070012#include "GrBatchTest.h"
joshualitt76e7fb62015-02-11 08:52:27 -080013#include "GrBufferAllocPool.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000014#include "GrDrawTarget.h"
joshualitteb2a6762014-12-04 11:35:33 -080015#include "GrGeometryProcessor.h"
egdaniel605dd0f2014-11-12 08:35:25 -080016#include "GrInvariantOutput.h"
egdaniel8dd688b2015-01-22 10:16:09 -080017#include "GrPipelineBuilder.h"
joshualitt76e7fb62015-02-11 08:52:27 -080018#include "GrProcessor.h"
bsalomoned0bcad2015-05-04 10:36:42 -070019#include "GrResourceProvider.h"
bsalomon72e3ae42015-04-28 08:08:46 -070020#include "GrVertexBuffer.h"
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000021#include "SkRRect.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000022#include "SkStrokeRec.h"
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +000023#include "SkTLazy.h"
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +000024#include "effects/GrRRectEffect.h"
joshualitteb2a6762014-12-04 11:35:33 -080025#include "gl/GrGLProcessor.h"
26#include "gl/GrGLSL.h"
27#include "gl/GrGLGeometryProcessor.h"
28#include "gl/builders/GrGLProgramBuilder.h"
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +000029
joshualitt76e7fb62015-02-11 08:52:27 -080030// TODO(joshualitt) - Break this file up during GrBatch post implementation cleanup
31
commit-bot@chromium.org81312832013-03-22 18:34:09 +000032namespace {
joshualitt5ead6da2014-10-22 16:00:29 -070033// TODO(joshualitt) add per vertex colors
commit-bot@chromium.org81312832013-03-22 18:34:09 +000034struct CircleVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000035 SkPoint fPos;
36 SkPoint fOffset;
commit-bot@chromium.org81312832013-03-22 18:34:09 +000037 SkScalar fOuterRadius;
38 SkScalar fInnerRadius;
39};
40
41struct EllipseVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000042 SkPoint fPos;
43 SkPoint fOffset;
44 SkPoint fOuterRadii;
45 SkPoint fInnerRadii;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000046};
47
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000048struct DIEllipseVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000049 SkPoint fPos;
50 SkPoint fOuterOffset;
51 SkPoint fInnerOffset;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000052};
53
commit-bot@chromium.org81312832013-03-22 18:34:09 +000054inline bool circle_stays_circle(const SkMatrix& m) {
55 return m.isSimilarity();
56}
57
58}
59
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000060///////////////////////////////////////////////////////////////////////////////
61
62/**
bsalomonce1c8862014-12-15 07:11:22 -080063 * The output of this effect is a modulation of the input color and coverage for a circle. It
64 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
65 * with origin at the circle center. Two vertex attributes are used:
66 * vec2f : position in device space of the bounding geometry vertices
67 * vec4f : (p.xy, outerRad, innerRad)
68 * p is the position in the normalized space.
69 * outerRad is the outerRadius in device space.
70 * innerRad is the innerRadius in normalized space (ignored if not stroking).
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000071 */
72
joshualitt249af152014-09-15 11:41:13 -070073class CircleEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000074public:
joshualittd27f73e2014-12-29 07:43:36 -080075 static GrGeometryProcessor* Create(GrColor color, bool stroke, const SkMatrix& localMatrix) {
76 return SkNEW_ARGS(CircleEdgeEffect, (color, stroke, localMatrix));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000077 }
78
joshualitt71c92602015-01-14 08:12:47 -080079 const Attribute* inPosition() const { return fInPosition; }
80 const Attribute* inCircleEdge() const { return fInCircleEdge; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000081 virtual ~CircleEdgeEffect() {}
82
mtklein36352bf2015-03-25 18:17:31 -070083 const char* name() const override { return "CircleEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000084
85 inline bool isStroked() const { return fStroke; }
86
joshualittb0a8a372014-09-23 09:50:21 -070087 class GLProcessor : public GrGLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000088 public:
joshualitteb2a6762014-12-04 11:35:33 -080089 GLProcessor(const GrGeometryProcessor&,
joshualitt9b989322014-12-15 14:16:27 -080090 const GrBatchTracker&)
91 : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000092
mtklein36352bf2015-03-25 18:17:31 -070093 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
joshualitt2dd1ae02014-12-03 06:24:10 -080094 const CircleEdgeEffect& ce = args.fGP.cast<CircleEdgeEffect>();
joshualitt9b989322014-12-15 14:16:27 -080095 GrGLGPBuilder* pb = args.fPB;
96 const BatchTracker& local = args.fBT.cast<BatchTracker>();
joshualitt2dd1ae02014-12-03 06:24:10 -080097 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
98
joshualittabb52a12015-01-13 15:02:10 -080099 // emit attributes
100 vsBuilder->emitAttributes(ce);
101
joshualitt74077b92014-10-24 11:26:03 -0700102 GrGLVertToFrag v(kVec4f_GrSLType);
103 args.fPB->addVarying("CircleEdge", &v);
joshualitt2dd1ae02014-12-03 06:24:10 -0800104 vsBuilder->codeAppendf("%s = %s;", v.vsOut(), ce.inCircleEdge()->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000105
joshualitt9b989322014-12-15 14:16:27 -0800106 // Setup pass through color
107 this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
108 &fColorUniform);
109
joshualittabb52a12015-01-13 15:02:10 -0800110 // Setup position
joshualittdd219872015-02-12 14:48:42 -0800111 this->setupPosition(pb, gpArgs, ce.inPosition()->fName, ce.viewMatrix());
joshualittabb52a12015-01-13 15:02:10 -0800112
113 // emit transforms
robertphillips46d36f02015-01-18 08:14:14 -0800114 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ce.inPosition()->fName,
tfarina567ff2f2015-04-27 07:01:44 -0700115 ce.localMatrix(), args.fTransformsIn, args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800116
egdaniel29bee0f2015-04-29 11:54:42 -0700117 GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt74077b92014-10-24 11:26:03 -0700118 fsBuilder->codeAppendf("float d = length(%s.xy);", v.fsIn());
bsalomonce1c8862014-12-15 07:11:22 -0800119 fsBuilder->codeAppendf("float edgeAlpha = clamp(%s.z * (1.0 - d), 0.0, 1.0);", v.fsIn());
joshualitt2dd1ae02014-12-03 06:24:10 -0800120 if (ce.isStroked()) {
bsalomonce1c8862014-12-15 07:11:22 -0800121 fsBuilder->codeAppendf("float innerAlpha = clamp(%s.z * (d - %s.w), 0.0, 1.0);",
122 v.fsIn(), v.fsIn());
joshualitt74077b92014-10-24 11:26:03 -0700123 fsBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000124 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000125
joshualitt2dd1ae02014-12-03 06:24:10 -0800126 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000127 }
128
robertphillips46d36f02015-01-18 08:14:14 -0800129 static void GenKey(const GrGeometryProcessor& gp,
joshualitt9b989322014-12-15 14:16:27 -0800130 const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700131 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700132 GrProcessorKeyBuilder* b) {
joshualitt9b989322014-12-15 14:16:27 -0800133 const BatchTracker& local = bt.cast<BatchTracker>();
robertphillips46d36f02015-01-18 08:14:14 -0800134 const CircleEdgeEffect& circleEffect = gp.cast<CircleEdgeEffect>();
joshualitt8fc6c2d2014-12-22 15:27:05 -0800135 uint16_t key = circleEffect.isStroked() ? 0x1 : 0x0;
robertphillips46d36f02015-01-18 08:14:14 -0800136 key |= local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x2 : 0x0;
137 key |= ComputePosKey(gp.viewMatrix()) << 2;
joshualitt8fc6c2d2014-12-22 15:27:05 -0800138 b->add32(key << 16 | local.fInputColorType);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000139 }
140
joshualitt9b989322014-12-15 14:16:27 -0800141 virtual void setData(const GrGLProgramDataManager& pdman,
142 const GrPrimitiveProcessor& gp,
mtklein36352bf2015-03-25 18:17:31 -0700143 const GrBatchTracker& bt) override {
joshualittee2af952014-12-30 09:04:15 -0800144 this->setUniformViewMatrix(pdman, gp.viewMatrix());
145
joshualitt9b989322014-12-15 14:16:27 -0800146 const BatchTracker& local = bt.cast<BatchTracker>();
147 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
148 GrGLfloat c[4];
149 GrColorToRGBAFloat(local.fColor, c);
150 pdman.set4fv(fColorUniform, 1, c);
151 fColor = local.fColor;
152 }
153 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000154
155 private:
joshualitt9b989322014-12-15 14:16:27 -0800156 GrColor fColor;
157 UniformHandle fColorUniform;
joshualitt249af152014-09-15 11:41:13 -0700158 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000159 };
160
joshualitteb2a6762014-12-04 11:35:33 -0800161 virtual void getGLProcessorKey(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700162 const GrGLSLCaps& caps,
mtklein36352bf2015-03-25 18:17:31 -0700163 GrProcessorKeyBuilder* b) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800164 GLProcessor::GenKey(*this, bt, caps, b);
165 }
166
joshualittabb52a12015-01-13 15:02:10 -0800167 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700168 const GrGLSLCaps&) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800169 return SkNEW_ARGS(GLProcessor, (*this, bt));
170 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000171
mtklein36352bf2015-03-25 18:17:31 -0700172 void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const override {
joshualitt9b989322014-12-15 14:16:27 -0800173 BatchTracker* local = bt->cast<BatchTracker>();
174 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
joshualitt290c09b2014-12-19 13:45:20 -0800175 local->fUsesLocalCoords = init.fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800176 }
177
joshualitt50cb76b2015-04-28 09:17:05 -0700178 bool onCanMakeEqual(const GrBatchTracker& m,
179 const GrGeometryProcessor& that,
180 const GrBatchTracker& t) const override {
181 const BatchTracker& mine = m.cast<BatchTracker>();
182 const BatchTracker& theirs = t.cast<BatchTracker>();
183 return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
184 that, theirs.fUsesLocalCoords) &&
185 CanCombineOutput(mine.fInputColorType, mine.fColor,
186 theirs.fInputColorType, theirs.fColor);
187 }
188
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000189private:
joshualittd27f73e2014-12-29 07:43:36 -0800190 CircleEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix)
joshualitt8059eb92014-12-29 15:10:07 -0800191 : INHERITED(color, SkMatrix::I(), localMatrix) {
joshualitteb2a6762014-12-04 11:35:33 -0800192 this->initClassID<CircleEdgeEffect>();
joshualitt71c92602015-01-14 08:12:47 -0800193 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
194 fInCircleEdge = &this->addVertexAttrib(Attribute("inCircleEdge",
joshualitt2dd1ae02014-12-03 06:24:10 -0800195 kVec4f_GrVertexAttribType));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000196 fStroke = stroke;
197 }
198
joshualitt50cb76b2015-04-28 09:17:05 -0700199 bool onIsEqual(const GrGeometryProcessor& other) const override {
200 const CircleEdgeEffect& cee = other.cast<CircleEdgeEffect>();
201 return cee.fStroke == fStroke;
202 }
203
204 void onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
205 out->setUnknownSingleComponent();
206 }
207
joshualitt9b989322014-12-15 14:16:27 -0800208 struct BatchTracker {
209 GrGPInput fInputColorType;
210 GrColor fColor;
joshualitt290c09b2014-12-19 13:45:20 -0800211 bool fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800212 };
213
joshualitt71c92602015-01-14 08:12:47 -0800214 const Attribute* fInPosition;
215 const Attribute* fInCircleEdge;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000216 bool fStroke;
217
joshualittb0a8a372014-09-23 09:50:21 -0700218 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000219
joshualitt249af152014-09-15 11:41:13 -0700220 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000221};
222
joshualittb0a8a372014-09-23 09:50:21 -0700223GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000224
joshualittb0a8a372014-09-23 09:50:21 -0700225GrGeometryProcessor* CircleEdgeEffect::TestCreate(SkRandom* random,
226 GrContext* context,
227 const GrDrawTargetCaps&,
228 GrTexture* textures[]) {
joshualitt8059eb92014-12-29 15:10:07 -0800229 return CircleEdgeEffect::Create(GrRandomColor(random),
230 random->nextBool(),
joshualitt4eaf9ce2015-04-28 13:31:18 -0700231 GrTest::TestMatrix(random));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000232}
233
234///////////////////////////////////////////////////////////////////////////////
235
236/**
237 * The output of this effect is a modulation of the input color and coverage for an axis-aligned
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000238 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
239 * in both x and y directions.
240 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000241 * We are using an implicit function of x^2/a^2 + y^2/b^2 - 1 = 0.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000242 */
243
joshualitt249af152014-09-15 11:41:13 -0700244class EllipseEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000245public:
joshualittd27f73e2014-12-29 07:43:36 -0800246 static GrGeometryProcessor* Create(GrColor color, bool stroke, const SkMatrix& localMatrix) {
247 return SkNEW_ARGS(EllipseEdgeEffect, (color, stroke, localMatrix));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000248 }
249
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000250 virtual ~EllipseEdgeEffect() {}
251
mtklein36352bf2015-03-25 18:17:31 -0700252 const char* name() const override { return "EllipseEdge"; }
joshualitt2dd1ae02014-12-03 06:24:10 -0800253
joshualitt71c92602015-01-14 08:12:47 -0800254 const Attribute* inPosition() const { return fInPosition; }
255 const Attribute* inEllipseOffset() const { return fInEllipseOffset; }
256 const Attribute* inEllipseRadii() const { return fInEllipseRadii; }
joshualitt249af152014-09-15 11:41:13 -0700257
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000258 inline bool isStroked() const { return fStroke; }
259
joshualittb0a8a372014-09-23 09:50:21 -0700260 class GLProcessor : public GrGLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000261 public:
joshualitteb2a6762014-12-04 11:35:33 -0800262 GLProcessor(const GrGeometryProcessor&,
joshualitt9b989322014-12-15 14:16:27 -0800263 const GrBatchTracker&)
264 : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000265
mtklein36352bf2015-03-25 18:17:31 -0700266 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
joshualitt2dd1ae02014-12-03 06:24:10 -0800267 const EllipseEdgeEffect& ee = args.fGP.cast<EllipseEdgeEffect>();
joshualitt9b989322014-12-15 14:16:27 -0800268 GrGLGPBuilder* pb = args.fPB;
269 const BatchTracker& local = args.fBT.cast<BatchTracker>();
joshualitt2dd1ae02014-12-03 06:24:10 -0800270 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000271
joshualittabb52a12015-01-13 15:02:10 -0800272 // emit attributes
273 vsBuilder->emitAttributes(ee);
274
joshualitt74077b92014-10-24 11:26:03 -0700275 GrGLVertToFrag ellipseOffsets(kVec2f_GrSLType);
276 args.fPB->addVarying("EllipseOffsets", &ellipseOffsets);
joshualitt74077b92014-10-24 11:26:03 -0700277 vsBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800278 ee.inEllipseOffset()->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000279
joshualitt74077b92014-10-24 11:26:03 -0700280 GrGLVertToFrag ellipseRadii(kVec4f_GrSLType);
281 args.fPB->addVarying("EllipseRadii", &ellipseRadii);
282 vsBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800283 ee.inEllipseRadii()->fName);
284
joshualitt9b989322014-12-15 14:16:27 -0800285 // Setup pass through color
286 this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
287 &fColorUniform);
288
joshualittabb52a12015-01-13 15:02:10 -0800289 // Setup position
joshualittdd219872015-02-12 14:48:42 -0800290 this->setupPosition(pb, gpArgs, ee.inPosition()->fName, ee.viewMatrix());
joshualittabb52a12015-01-13 15:02:10 -0800291
292 // emit transforms
robertphillips46d36f02015-01-18 08:14:14 -0800293 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ee.inPosition()->fName,
joshualittabb52a12015-01-13 15:02:10 -0800294 ee.localMatrix(), args.fTransformsIn, args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800295
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000296 // for outer curve
egdaniel29bee0f2015-04-29 11:54:42 -0700297 GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt74077b92014-10-24 11:26:03 -0700298 fsBuilder->codeAppendf("vec2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
299 ellipseRadii.fsIn());
300 fsBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
301 fsBuilder->codeAppendf("vec2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
302 fsBuilder->codeAppend("float grad_dot = dot(grad, grad);");
303
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000304 // avoid calling inversesqrt on zero.
joshualitt74077b92014-10-24 11:26:03 -0700305 fsBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
306 fsBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
307 fsBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000308
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000309 // for inner curve
joshualitt2dd1ae02014-12-03 06:24:10 -0800310 if (ee.isStroked()) {
joshualitt74077b92014-10-24 11:26:03 -0700311 fsBuilder->codeAppendf("scaledOffset = %s*%s.zw;",
312 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
313 fsBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
314 fsBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;",
315 ellipseRadii.fsIn());
316 fsBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
317 fsBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000318 }
319
joshualitt2dd1ae02014-12-03 06:24:10 -0800320 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000321 }
322
robertphillips46d36f02015-01-18 08:14:14 -0800323 static void GenKey(const GrGeometryProcessor& gp,
joshualitt9b989322014-12-15 14:16:27 -0800324 const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700325 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700326 GrProcessorKeyBuilder* b) {
joshualitt9b989322014-12-15 14:16:27 -0800327 const BatchTracker& local = bt.cast<BatchTracker>();
robertphillips46d36f02015-01-18 08:14:14 -0800328 const EllipseEdgeEffect& ellipseEffect = gp.cast<EllipseEdgeEffect>();
joshualitt8fc6c2d2014-12-22 15:27:05 -0800329 uint16_t key = ellipseEffect.isStroked() ? 0x1 : 0x0;
robertphillips46d36f02015-01-18 08:14:14 -0800330 key |= local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x2 : 0x0;
331 key |= ComputePosKey(gp.viewMatrix()) << 2;
joshualitt8fc6c2d2014-12-22 15:27:05 -0800332 b->add32(key << 16 | local.fInputColorType);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000333 }
334
joshualitt9b989322014-12-15 14:16:27 -0800335 virtual void setData(const GrGLProgramDataManager& pdman,
336 const GrPrimitiveProcessor& gp,
mtklein36352bf2015-03-25 18:17:31 -0700337 const GrBatchTracker& bt) override {
joshualittee2af952014-12-30 09:04:15 -0800338 this->setUniformViewMatrix(pdman, gp.viewMatrix());
339
joshualitt9b989322014-12-15 14:16:27 -0800340 const BatchTracker& local = bt.cast<BatchTracker>();
341 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
342 GrGLfloat c[4];
343 GrColorToRGBAFloat(local.fColor, c);
344 pdman.set4fv(fColorUniform, 1, c);
345 fColor = local.fColor;
346 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000347 }
348
349 private:
joshualitt9b989322014-12-15 14:16:27 -0800350 GrColor fColor;
351 UniformHandle fColorUniform;
352
joshualitt249af152014-09-15 11:41:13 -0700353 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000354 };
355
joshualitteb2a6762014-12-04 11:35:33 -0800356 virtual void getGLProcessorKey(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700357 const GrGLSLCaps& caps,
mtklein36352bf2015-03-25 18:17:31 -0700358 GrProcessorKeyBuilder* b) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800359 GLProcessor::GenKey(*this, bt, caps, b);
360 }
361
joshualittabb52a12015-01-13 15:02:10 -0800362 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700363 const GrGLSLCaps&) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800364 return SkNEW_ARGS(GLProcessor, (*this, bt));
365 }
366
mtklein36352bf2015-03-25 18:17:31 -0700367 void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const override {
joshualitt9b989322014-12-15 14:16:27 -0800368 BatchTracker* local = bt->cast<BatchTracker>();
369 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
joshualitt290c09b2014-12-19 13:45:20 -0800370 local->fUsesLocalCoords = init.fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800371 }
372
joshualitt50cb76b2015-04-28 09:17:05 -0700373 bool onCanMakeEqual(const GrBatchTracker& m,
374 const GrGeometryProcessor& that,
375 const GrBatchTracker& t) const override {
376 const BatchTracker& mine = m.cast<BatchTracker>();
377 const BatchTracker& theirs = t.cast<BatchTracker>();
378 return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
379 that, theirs.fUsesLocalCoords) &&
380 CanCombineOutput(mine.fInputColorType, mine.fColor,
381 theirs.fInputColorType, theirs.fColor);
382 }
383
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000384private:
joshualittd27f73e2014-12-29 07:43:36 -0800385 EllipseEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix)
joshualitt8059eb92014-12-29 15:10:07 -0800386 : INHERITED(color, SkMatrix::I(), localMatrix) {
joshualitteb2a6762014-12-04 11:35:33 -0800387 this->initClassID<EllipseEdgeEffect>();
joshualitt71c92602015-01-14 08:12:47 -0800388 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
389 fInEllipseOffset = &this->addVertexAttrib(Attribute("inEllipseOffset",
joshualitt2dd1ae02014-12-03 06:24:10 -0800390 kVec2f_GrVertexAttribType));
joshualitt71c92602015-01-14 08:12:47 -0800391 fInEllipseRadii = &this->addVertexAttrib(Attribute("inEllipseRadii",
joshualitt2dd1ae02014-12-03 06:24:10 -0800392 kVec4f_GrVertexAttribType));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000393 fStroke = stroke;
394 }
395
joshualitt50cb76b2015-04-28 09:17:05 -0700396 bool onIsEqual(const GrGeometryProcessor& other) const override {
397 const EllipseEdgeEffect& eee = other.cast<EllipseEdgeEffect>();
398 return eee.fStroke == fStroke;
399 }
400
401 void onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
402 out->setUnknownSingleComponent();
403 }
404
joshualitt9b989322014-12-15 14:16:27 -0800405 struct BatchTracker {
406 GrGPInput fInputColorType;
407 GrColor fColor;
joshualitt290c09b2014-12-19 13:45:20 -0800408 bool fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800409 };
410
joshualitt71c92602015-01-14 08:12:47 -0800411 const Attribute* fInPosition;
412 const Attribute* fInEllipseOffset;
413 const Attribute* fInEllipseRadii;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000414 bool fStroke;
415
joshualittb0a8a372014-09-23 09:50:21 -0700416 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000417
joshualitt249af152014-09-15 11:41:13 -0700418 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000419};
420
joshualittb0a8a372014-09-23 09:50:21 -0700421GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000422
joshualittb0a8a372014-09-23 09:50:21 -0700423GrGeometryProcessor* EllipseEdgeEffect::TestCreate(SkRandom* random,
424 GrContext* context,
425 const GrDrawTargetCaps&,
426 GrTexture* textures[]) {
joshualitt8059eb92014-12-29 15:10:07 -0800427 return EllipseEdgeEffect::Create(GrRandomColor(random),
428 random->nextBool(),
joshualitt4eaf9ce2015-04-28 13:31:18 -0700429 GrTest::TestMatrix(random));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000430}
431
432///////////////////////////////////////////////////////////////////////////////
433
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000434/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000435 * The output of this effect is a modulation of the input color and coverage for an ellipse,
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000436 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
437 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
438 * using differentials.
439 *
440 * The result is device-independent and can be used with any affine matrix.
441 */
442
joshualitt249af152014-09-15 11:41:13 -0700443class DIEllipseEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000444public:
445 enum Mode { kStroke = 0, kHairline, kFill };
446
joshualitt8059eb92014-12-29 15:10:07 -0800447 static GrGeometryProcessor* Create(GrColor color, const SkMatrix& viewMatrix, Mode mode) {
448 return SkNEW_ARGS(DIEllipseEdgeEffect, (color, viewMatrix, mode));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000449 }
450
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000451 virtual ~DIEllipseEdgeEffect() {}
452
mtklein36352bf2015-03-25 18:17:31 -0700453 const char* name() const override { return "DIEllipseEdge"; }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000454
joshualitt71c92602015-01-14 08:12:47 -0800455 const Attribute* inPosition() const { return fInPosition; }
456 const Attribute* inEllipseOffsets0() const { return fInEllipseOffsets0; }
457 const Attribute* inEllipseOffsets1() const { return fInEllipseOffsets1; }
joshualitt249af152014-09-15 11:41:13 -0700458
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000459 inline Mode getMode() const { return fMode; }
460
joshualittb0a8a372014-09-23 09:50:21 -0700461 class GLProcessor : public GrGLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000462 public:
joshualitteb2a6762014-12-04 11:35:33 -0800463 GLProcessor(const GrGeometryProcessor&,
joshualitt9b989322014-12-15 14:16:27 -0800464 const GrBatchTracker&)
465 : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000466
mtklein36352bf2015-03-25 18:17:31 -0700467 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
joshualitt2dd1ae02014-12-03 06:24:10 -0800468 const DIEllipseEdgeEffect& ee = args.fGP.cast<DIEllipseEdgeEffect>();
joshualitt9b989322014-12-15 14:16:27 -0800469 GrGLGPBuilder* pb = args.fPB;
470 const BatchTracker& local = args.fBT.cast<BatchTracker>();
joshualitt2dd1ae02014-12-03 06:24:10 -0800471 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000472
joshualittabb52a12015-01-13 15:02:10 -0800473 // emit attributes
474 vsBuilder->emitAttributes(ee);
475
joshualitt74077b92014-10-24 11:26:03 -0700476 GrGLVertToFrag offsets0(kVec2f_GrSLType);
477 args.fPB->addVarying("EllipseOffsets0", &offsets0);
joshualitt74077b92014-10-24 11:26:03 -0700478 vsBuilder->codeAppendf("%s = %s;", offsets0.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800479 ee.inEllipseOffsets0()->fName);
joshualitt74077b92014-10-24 11:26:03 -0700480
481 GrGLVertToFrag offsets1(kVec2f_GrSLType);
482 args.fPB->addVarying("EllipseOffsets1", &offsets1);
483 vsBuilder->codeAppendf("%s = %s;", offsets1.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800484 ee.inEllipseOffsets1()->fName);
485
joshualitt9b989322014-12-15 14:16:27 -0800486 // Setup pass through color
487 this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
488 &fColorUniform);
489
joshualittabb52a12015-01-13 15:02:10 -0800490 // Setup position
joshualittdd219872015-02-12 14:48:42 -0800491 this->setupPosition(pb, gpArgs, ee.inPosition()->fName, ee.viewMatrix());
joshualittabb52a12015-01-13 15:02:10 -0800492
493 // emit transforms
robertphillips46d36f02015-01-18 08:14:14 -0800494 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ee.inPosition()->fName,
joshualittabb52a12015-01-13 15:02:10 -0800495 ee.localMatrix(), args.fTransformsIn, args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800496
egdaniel29bee0f2015-04-29 11:54:42 -0700497 GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt30ba4362014-08-21 20:18:45 -0700498 SkAssertResult(fsBuilder->enableFeature(
499 GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000500 // for outer curve
joshualitt74077b92014-10-24 11:26:03 -0700501 fsBuilder->codeAppendf("vec2 scaledOffset = %s.xy;", offsets0.fsIn());
502 fsBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
503 fsBuilder->codeAppendf("vec2 duvdx = dFdx(%s);", offsets0.fsIn());
504 fsBuilder->codeAppendf("vec2 duvdy = dFdy(%s);", offsets0.fsIn());
505 fsBuilder->codeAppendf("vec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
506 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
507 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000508
joshualitt74077b92014-10-24 11:26:03 -0700509 fsBuilder->codeAppend("float grad_dot = dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000510 // avoid calling inversesqrt on zero.
joshualitt74077b92014-10-24 11:26:03 -0700511 fsBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
512 fsBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
joshualitt2dd1ae02014-12-03 06:24:10 -0800513 if (kHairline == ee.getMode()) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000514 // can probably do this with one step
joshualitt74077b92014-10-24 11:26:03 -0700515 fsBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
516 fsBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000517 } else {
joshualitt74077b92014-10-24 11:26:03 -0700518 fsBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000519 }
520
521 // for inner curve
joshualitt2dd1ae02014-12-03 06:24:10 -0800522 if (kStroke == ee.getMode()) {
joshualitt74077b92014-10-24 11:26:03 -0700523 fsBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
524 fsBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
525 fsBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
526 fsBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
527 fsBuilder->codeAppendf("grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
528 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
529 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(),
530 offsets1.fsIn());
531 fsBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
532 fsBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000533 }
534
joshualitt2dd1ae02014-12-03 06:24:10 -0800535 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000536 }
537
robertphillips46d36f02015-01-18 08:14:14 -0800538 static void GenKey(const GrGeometryProcessor& gp,
joshualitt9b989322014-12-15 14:16:27 -0800539 const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700540 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700541 GrProcessorKeyBuilder* b) {
joshualitt9b989322014-12-15 14:16:27 -0800542 const BatchTracker& local = bt.cast<BatchTracker>();
robertphillips46d36f02015-01-18 08:14:14 -0800543 const DIEllipseEdgeEffect& ellipseEffect = gp.cast<DIEllipseEdgeEffect>();
joshualitt8fc6c2d2014-12-22 15:27:05 -0800544 uint16_t key = ellipseEffect.getMode();
robertphillips46d36f02015-01-18 08:14:14 -0800545 key |= local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x1 << 8 : 0x0;
546 key |= ComputePosKey(gp.viewMatrix()) << 9;
joshualitt8fc6c2d2014-12-22 15:27:05 -0800547 b->add32(key << 16 | local.fInputColorType);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000548 }
549
joshualitt9b989322014-12-15 14:16:27 -0800550 virtual void setData(const GrGLProgramDataManager& pdman,
551 const GrPrimitiveProcessor& gp,
mtklein36352bf2015-03-25 18:17:31 -0700552 const GrBatchTracker& bt) override {
joshualittee2af952014-12-30 09:04:15 -0800553 this->setUniformViewMatrix(pdman, gp.viewMatrix());
554
joshualitt9b989322014-12-15 14:16:27 -0800555 const BatchTracker& local = bt.cast<BatchTracker>();
556 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
557 GrGLfloat c[4];
558 GrColorToRGBAFloat(local.fColor, c);
559 pdman.set4fv(fColorUniform, 1, c);
560 fColor = local.fColor;
561 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000562 }
563
564 private:
joshualitt9b989322014-12-15 14:16:27 -0800565 GrColor fColor;
566 UniformHandle fColorUniform;
567
joshualitt249af152014-09-15 11:41:13 -0700568 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000569 };
570
joshualitteb2a6762014-12-04 11:35:33 -0800571 virtual void getGLProcessorKey(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700572 const GrGLSLCaps& caps,
mtklein36352bf2015-03-25 18:17:31 -0700573 GrProcessorKeyBuilder* b) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800574 GLProcessor::GenKey(*this, bt, caps, b);
575 }
576
joshualittabb52a12015-01-13 15:02:10 -0800577 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700578 const GrGLSLCaps&) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800579 return SkNEW_ARGS(GLProcessor, (*this, bt));
580 }
581
mtklein36352bf2015-03-25 18:17:31 -0700582 void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const override {
joshualitt9b989322014-12-15 14:16:27 -0800583 BatchTracker* local = bt->cast<BatchTracker>();
584 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
joshualitt290c09b2014-12-19 13:45:20 -0800585 local->fUsesLocalCoords = init.fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800586 }
587
joshualitt50cb76b2015-04-28 09:17:05 -0700588 bool onCanMakeEqual(const GrBatchTracker& m,
589 const GrGeometryProcessor& that,
590 const GrBatchTracker& t) const override {
591 const BatchTracker& mine = m.cast<BatchTracker>();
592 const BatchTracker& theirs = t.cast<BatchTracker>();
593 return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
594 that, theirs.fUsesLocalCoords) &&
595 CanCombineOutput(mine.fInputColorType, mine.fColor,
596 theirs.fInputColorType, theirs.fColor);
597 }
598
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000599private:
joshualitt8059eb92014-12-29 15:10:07 -0800600 DIEllipseEdgeEffect(GrColor color, const SkMatrix& viewMatrix, Mode mode)
601 : INHERITED(color, viewMatrix) {
joshualitteb2a6762014-12-04 11:35:33 -0800602 this->initClassID<DIEllipseEdgeEffect>();
joshualitt71c92602015-01-14 08:12:47 -0800603 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
604 fInEllipseOffsets0 = &this->addVertexAttrib(Attribute("inEllipseOffsets0",
joshualitt2dd1ae02014-12-03 06:24:10 -0800605 kVec2f_GrVertexAttribType));
joshualitt71c92602015-01-14 08:12:47 -0800606 fInEllipseOffsets1 = &this->addVertexAttrib(Attribute("inEllipseOffsets1",
joshualitt2dd1ae02014-12-03 06:24:10 -0800607 kVec2f_GrVertexAttribType));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000608 fMode = mode;
609 }
610
joshualitt50cb76b2015-04-28 09:17:05 -0700611 bool onIsEqual(const GrGeometryProcessor& other) const override {
612 const DIEllipseEdgeEffect& eee = other.cast<DIEllipseEdgeEffect>();
613 return eee.fMode == fMode;
614 }
615
616 void onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
617 out->setUnknownSingleComponent();
618 }
619
joshualitt9b989322014-12-15 14:16:27 -0800620 struct BatchTracker {
621 GrGPInput fInputColorType;
622 GrColor fColor;
joshualitt290c09b2014-12-19 13:45:20 -0800623 bool fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800624 };
625
joshualitt71c92602015-01-14 08:12:47 -0800626 const Attribute* fInPosition;
627 const Attribute* fInEllipseOffsets0;
628 const Attribute* fInEllipseOffsets1;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000629 Mode fMode;
630
joshualittb0a8a372014-09-23 09:50:21 -0700631 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000632
joshualitt249af152014-09-15 11:41:13 -0700633 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000634};
635
joshualittb0a8a372014-09-23 09:50:21 -0700636GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseEdgeEffect);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000637
joshualittb0a8a372014-09-23 09:50:21 -0700638GrGeometryProcessor* DIEllipseEdgeEffect::TestCreate(SkRandom* random,
639 GrContext* context,
640 const GrDrawTargetCaps&,
641 GrTexture* textures[]) {
joshualitt8059eb92014-12-29 15:10:07 -0800642 return DIEllipseEdgeEffect::Create(GrRandomColor(random),
joshualitt4eaf9ce2015-04-28 13:31:18 -0700643 GrTest::TestMatrix(random),
joshualitt8059eb92014-12-29 15:10:07 -0800644 (Mode)(random->nextRangeU(0,2)));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000645}
646
647///////////////////////////////////////////////////////////////////////////////
648
joshualitt9853cce2014-11-17 14:22:48 -0800649bool GrOvalRenderer::drawOval(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -0800650 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -0800651 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800652 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -0800653 bool useAA,
654 const SkRect& oval,
655 const SkStrokeRec& stroke)
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000656{
bsalomoned0bcad2015-05-04 10:36:42 -0700657 bool useCoverageAA = useAA && !pipelineBuilder->getRenderTarget()->isMultisampled();
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000658
659 if (!useCoverageAA) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000660 return false;
661 }
662
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000663 // we can draw circles
joshualitt8059eb92014-12-29 15:10:07 -0800664 if (SkScalarNearlyEqual(oval.width(), oval.height()) && circle_stays_circle(viewMatrix)) {
egdaniel8dd688b2015-01-22 10:16:09 -0800665 this->drawCircle(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval, stroke);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000666 // if we have shader derivative support, render as device-independent
jvanverthe9c0fc62015-04-29 11:18:05 -0700667 } else if (target->caps()->shaderCaps()->shaderDerivativeSupport()) {
egdaniel8dd688b2015-01-22 10:16:09 -0800668 return this->drawDIEllipse(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval,
joshualitt8059eb92014-12-29 15:10:07 -0800669 stroke);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000670 // otherwise axis-aligned ellipses only
joshualitt8059eb92014-12-29 15:10:07 -0800671 } else if (viewMatrix.rectStaysRect()) {
egdaniel8dd688b2015-01-22 10:16:09 -0800672 return this->drawEllipse(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval,
673 stroke);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000674 } else {
675 return false;
676 }
677
678 return true;
679}
680
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000681///////////////////////////////////////////////////////////////////////////////
682
joshualitt76e7fb62015-02-11 08:52:27 -0800683class CircleBatch : public GrBatch {
684public:
685 struct Geometry {
686 GrColor fColor;
687 SkMatrix fViewMatrix;
688 SkScalar fInnerRadius;
689 SkScalar fOuterRadius;
690 bool fStroke;
egdanielbc227142015-04-21 06:28:08 -0700691 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800692 };
693
bsalomoned0bcad2015-05-04 10:36:42 -0700694 static GrBatch* Create(const Geometry& geometry) { return SkNEW_ARGS(CircleBatch, (geometry)); }
joshualitt76e7fb62015-02-11 08:52:27 -0800695
mtklein36352bf2015-03-25 18:17:31 -0700696 const char* name() const override { return "CircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800697
mtklein36352bf2015-03-25 18:17:31 -0700698 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800699 // When this is called on a batch, there is only one geometry bundle
700 out->setKnownFourComponents(fGeoData[0].fColor);
701 }
702
mtklein36352bf2015-03-25 18:17:31 -0700703 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800704 out->setUnknownSingleComponent();
705 }
706
mtklein36352bf2015-03-25 18:17:31 -0700707 void initBatchTracker(const GrPipelineInfo& init) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800708 // Handle any color overrides
709 if (init.fColorIgnored) {
710 fGeoData[0].fColor = GrColor_ILLEGAL;
711 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
712 fGeoData[0].fColor = init.fOverrideColor;
713 }
714
715 // setup batch properties
716 fBatch.fColorIgnored = init.fColorIgnored;
717 fBatch.fColor = fGeoData[0].fColor;
718 fBatch.fStroke = fGeoData[0].fStroke;
719 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
720 fBatch.fCoverageIgnored = init.fCoverageIgnored;
721 }
722
mtklein36352bf2015-03-25 18:17:31 -0700723 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800724 SkMatrix invert;
725 if (!this->viewMatrix().invert(&invert)) {
726 return;
727 }
728
729 // Setup geometry processor
730 SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(),
731 this->stroke(),
732 invert));
733
734 batchTarget->initDraw(gp, pipeline);
735
736 // TODO this is hacky, but the only way we have to initialize the GP is to use the
737 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
738 // everywhere we can remove this nastiness
739 GrPipelineInfo init;
740 init.fColorIgnored = fBatch.fColorIgnored;
741 init.fOverrideColor = GrColor_ILLEGAL;
742 init.fCoverageIgnored = fBatch.fCoverageIgnored;
743 init.fUsesLocalCoords = this->usesLocalCoords();
744 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
745
746 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -0800747 size_t vertexStride = gp->getVertexStride();
748 SkASSERT(vertexStride == sizeof(CircleVertex));
bsalomonb5238a72015-05-05 07:49:49 -0700749 QuadHelper helper;
750 CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(batchTarget, vertexStride,
751 instanceCount));
752 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -0800753 return;
754 }
755
joshualitt76e7fb62015-02-11 08:52:27 -0800756 for (int i = 0; i < instanceCount; i++) {
bsalomonb5238a72015-05-05 07:49:49 -0700757 Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -0800758
bsalomonb5238a72015-05-05 07:49:49 -0700759 SkScalar innerRadius = geom.fInnerRadius;
760 SkScalar outerRadius = geom.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800761
bsalomonb5238a72015-05-05 07:49:49 -0700762 const SkRect& bounds = geom.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800763
764 // The inner radius in the vertex data must be specified in normalized space.
765 innerRadius = innerRadius / outerRadius;
766 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
767 verts[0].fOffset = SkPoint::Make(-1, -1);
768 verts[0].fOuterRadius = outerRadius;
769 verts[0].fInnerRadius = innerRadius;
770
771 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
772 verts[1].fOffset = SkPoint::Make(-1, 1);
773 verts[1].fOuterRadius = outerRadius;
774 verts[1].fInnerRadius = innerRadius;
775
776 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
777 verts[2].fOffset = SkPoint::Make(1, 1);
778 verts[2].fOuterRadius = outerRadius;
779 verts[2].fInnerRadius = innerRadius;
780
781 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
782 verts[3].fOffset = SkPoint::Make(1, -1);
783 verts[3].fOuterRadius = outerRadius;
784 verts[3].fInnerRadius = innerRadius;
785
bsalomonb5238a72015-05-05 07:49:49 -0700786 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -0800787 }
bsalomonb5238a72015-05-05 07:49:49 -0700788 helper.issueDraws(batchTarget);
joshualitt76e7fb62015-02-11 08:52:27 -0800789 }
790
791 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
792
793private:
794 CircleBatch(const Geometry& geometry) {
795 this->initClassID<CircleBatch>();
796 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -0700797
798 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -0800799 }
800
mtklein36352bf2015-03-25 18:17:31 -0700801 bool onCombineIfPossible(GrBatch* t) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800802 CircleBatch* that = t->cast<CircleBatch>();
803
804 // TODO use vertex color to avoid breaking batches
805 if (this->color() != that->color()) {
806 return false;
807 }
808
809 if (this->stroke() != that->stroke()) {
810 return false;
811 }
812
813 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
814 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
815 return false;
816 }
817
818 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -0700819 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -0800820 return true;
821 }
822
823 GrColor color() const { return fBatch.fColor; }
824 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
825 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
826 bool stroke() const { return fBatch.fStroke; }
827
828 struct BatchTracker {
829 GrColor fColor;
830 bool fStroke;
831 bool fUsesLocalCoords;
832 bool fColorIgnored;
833 bool fCoverageIgnored;
834 };
835
joshualitt76e7fb62015-02-11 08:52:27 -0800836 BatchTracker fBatch;
837 SkSTArray<1, Geometry, true> fGeoData;
838};
839
joshualitt3e708c52015-04-30 13:49:27 -0700840static GrBatch* create_circle_batch(GrColor color,
841 const SkMatrix& viewMatrix,
842 bool useCoverageAA,
843 const SkRect& circle,
joshualittd96a67b2015-05-05 14:09:05 -0700844 const SkStrokeRec& stroke) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000845 SkPoint center = SkPoint::Make(circle.centerX(), circle.centerY());
joshualitt8059eb92014-12-29 15:10:07 -0800846 viewMatrix.mapPoints(&center, 1);
847 SkScalar radius = viewMatrix.mapRadius(SkScalarHalf(circle.width()));
848 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000849
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000850 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000851 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
852 SkStrokeRec::kHairline_Style == style;
853 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000854
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000855 SkScalar innerRadius = 0.0f;
856 SkScalar outerRadius = radius;
857 SkScalar halfWidth = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000858 if (hasStroke) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000859 if (SkScalarNearlyZero(strokeWidth)) {
860 halfWidth = SK_ScalarHalf;
861 } else {
862 halfWidth = SkScalarHalf(strokeWidth);
863 }
864
865 outerRadius += halfWidth;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000866 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000867 innerRadius = radius - halfWidth;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000868 }
869 }
870
bsalomonce1c8862014-12-15 07:11:22 -0800871 // The radii are outset for two reasons. First, it allows the shader to simply perform simpler
872 // computation because the computed alpha is zero, rather than 50%, at the radius.
873 // Second, the outer radius is used to compute the verts of the bounding box that is rendered
874 // and the outset ensures the box will cover all partially covered by the circle.
bsalomon@google.com58e30fe2013-04-01 19:01:20 +0000875 outerRadius += SK_ScalarHalf;
876 innerRadius -= SK_ScalarHalf;
877
joshualitt76e7fb62015-02-11 08:52:27 -0800878 CircleBatch::Geometry geometry;
879 geometry.fViewMatrix = viewMatrix;
880 geometry.fColor = color;
881 geometry.fInnerRadius = innerRadius;
882 geometry.fOuterRadius = outerRadius;
883 geometry.fStroke = isStrokeOnly && innerRadius > 0;
joshualittd96a67b2015-05-05 14:09:05 -0700884 geometry.fDevBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
885 center.fX + outerRadius, center.fY + outerRadius);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000886
joshualitt3e708c52015-04-30 13:49:27 -0700887 return CircleBatch::Create(geometry);
888}
889
890void GrOvalRenderer::drawCircle(GrDrawTarget* target,
891 GrPipelineBuilder* pipelineBuilder,
892 GrColor color,
893 const SkMatrix& viewMatrix,
894 bool useCoverageAA,
895 const SkRect& circle,
896 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -0700897 SkAutoTUnref<GrBatch> batch(create_circle_batch(color, viewMatrix, useCoverageAA, circle,
joshualittd96a67b2015-05-05 14:09:05 -0700898 stroke));
joshualitt99c7c072015-05-01 13:43:30 -0700899 target->drawBatch(pipelineBuilder, batch);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000900}
901
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000902///////////////////////////////////////////////////////////////////////////////
903
joshualitt76e7fb62015-02-11 08:52:27 -0800904class EllipseBatch : public GrBatch {
905public:
906 struct Geometry {
907 GrColor fColor;
908 SkMatrix fViewMatrix;
909 SkScalar fXRadius;
910 SkScalar fYRadius;
911 SkScalar fInnerXRadius;
912 SkScalar fInnerYRadius;
913 bool fStroke;
egdanielbc227142015-04-21 06:28:08 -0700914 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800915 };
916
917 static GrBatch* Create(const Geometry& geometry) {
918 return SkNEW_ARGS(EllipseBatch, (geometry));
919 }
920
mtklein36352bf2015-03-25 18:17:31 -0700921 const char* name() const override { return "EllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800922
mtklein36352bf2015-03-25 18:17:31 -0700923 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800924 // When this is called on a batch, there is only one geometry bundle
925 out->setKnownFourComponents(fGeoData[0].fColor);
926 }
mtklein36352bf2015-03-25 18:17:31 -0700927 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800928 out->setUnknownSingleComponent();
929 }
930
mtklein36352bf2015-03-25 18:17:31 -0700931 void initBatchTracker(const GrPipelineInfo& init) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800932 // Handle any color overrides
933 if (init.fColorIgnored) {
934 fGeoData[0].fColor = GrColor_ILLEGAL;
935 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
936 fGeoData[0].fColor = init.fOverrideColor;
937 }
938
939 // setup batch properties
940 fBatch.fColorIgnored = init.fColorIgnored;
941 fBatch.fColor = fGeoData[0].fColor;
942 fBatch.fStroke = fGeoData[0].fStroke;
943 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
944 fBatch.fCoverageIgnored = init.fCoverageIgnored;
945 }
946
mtklein36352bf2015-03-25 18:17:31 -0700947 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800948 SkMatrix invert;
949 if (!this->viewMatrix().invert(&invert)) {
950 return;
951 }
952
953 // Setup geometry processor
954 SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(),
955 this->stroke(),
956 invert));
joshualitt76e7fb62015-02-11 08:52:27 -0800957
958 batchTarget->initDraw(gp, pipeline);
959
960 // TODO this is hacky, but the only way we have to initialize the GP is to use the
961 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
962 // everywhere we can remove this nastiness
963 GrPipelineInfo init;
964 init.fColorIgnored = fBatch.fColorIgnored;
965 init.fOverrideColor = GrColor_ILLEGAL;
966 init.fCoverageIgnored = fBatch.fCoverageIgnored;
967 init.fUsesLocalCoords = this->usesLocalCoords();
968 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
969
970 int instanceCount = fGeoData.count();
bsalomonb5238a72015-05-05 07:49:49 -0700971 QuadHelper helper;
joshualitt76e7fb62015-02-11 08:52:27 -0800972 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -0800973 SkASSERT(vertexStride == sizeof(EllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -0700974 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
975 helper.init(batchTarget, vertexStride, instanceCount));
976 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -0800977 return;
978 }
979
bsalomon8415abe2015-05-04 11:41:41 -0700980 for (int i = 0; i < instanceCount; i++) {
bsalomonb5238a72015-05-05 07:49:49 -0700981 Geometry& geom = fGeoData[i];
bsalomon8415abe2015-05-04 11:41:41 -0700982
bsalomonb5238a72015-05-05 07:49:49 -0700983 SkScalar xRadius = geom.fXRadius;
984 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800985
986 // Compute the reciprocals of the radii here to save time in the shader
987 SkScalar xRadRecip = SkScalarInvert(xRadius);
988 SkScalar yRadRecip = SkScalarInvert(yRadius);
bsalomonb5238a72015-05-05 07:49:49 -0700989 SkScalar xInnerRadRecip = SkScalarInvert(geom.fInnerXRadius);
990 SkScalar yInnerRadRecip = SkScalarInvert(geom.fInnerYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -0800991
bsalomonb5238a72015-05-05 07:49:49 -0700992 const SkRect& bounds = geom.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800993
994 // The inner radius in the vertex data must be specified in normalized space.
995 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
996 verts[0].fOffset = SkPoint::Make(-xRadius, -yRadius);
997 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
998 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
999
1000 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
1001 verts[1].fOffset = SkPoint::Make(-xRadius, yRadius);
1002 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1003 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1004
1005 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
1006 verts[2].fOffset = SkPoint::Make(xRadius, yRadius);
1007 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1008 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1009
1010 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
1011 verts[3].fOffset = SkPoint::Make(xRadius, -yRadius);
1012 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1013 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1014
bsalomonb5238a72015-05-05 07:49:49 -07001015 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001016 }
bsalomonb5238a72015-05-05 07:49:49 -07001017 helper.issueDraws(batchTarget);
joshualitt76e7fb62015-02-11 08:52:27 -08001018 }
1019
1020 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1021
1022private:
1023 EllipseBatch(const Geometry& geometry) {
1024 this->initClassID<EllipseBatch>();
1025 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -07001026
1027 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001028 }
1029
mtklein36352bf2015-03-25 18:17:31 -07001030 bool onCombineIfPossible(GrBatch* t) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001031 EllipseBatch* that = t->cast<EllipseBatch>();
1032
1033 // TODO use vertex color to avoid breaking batches
1034 if (this->color() != that->color()) {
1035 return false;
1036 }
1037
1038 if (this->stroke() != that->stroke()) {
1039 return false;
1040 }
1041
1042 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1043 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1044 return false;
1045 }
1046
1047 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -07001048 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -08001049 return true;
1050 }
1051
1052 GrColor color() const { return fBatch.fColor; }
1053 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1054 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1055 bool stroke() const { return fBatch.fStroke; }
1056
1057 struct BatchTracker {
1058 GrColor fColor;
1059 bool fStroke;
1060 bool fUsesLocalCoords;
1061 bool fColorIgnored;
1062 bool fCoverageIgnored;
1063 };
1064
joshualitt76e7fb62015-02-11 08:52:27 -08001065 BatchTracker fBatch;
1066 SkSTArray<1, Geometry, true> fGeoData;
1067};
1068
joshualitt3e708c52015-04-30 13:49:27 -07001069static GrBatch* create_ellipse_batch(GrColor color,
1070 const SkMatrix& viewMatrix,
1071 bool useCoverageAA,
1072 const SkRect& ellipse,
joshualittd96a67b2015-05-05 14:09:05 -07001073 const SkStrokeRec& stroke) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001074#ifdef SK_DEBUG
1075 {
1076 // we should have checked for this previously
joshualitt8059eb92014-12-29 15:10:07 -08001077 bool isAxisAlignedEllipse = viewMatrix.rectStaysRect();
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00001078 SkASSERT(useCoverageAA && isAxisAlignedEllipse);
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001079 }
1080#endif
1081
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001082 // do any matrix crunching before we reset the draw state for device coords
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +00001083 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
joshualitt8059eb92014-12-29 15:10:07 -08001084 viewMatrix.mapPoints(&center, 1);
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001085 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1086 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
joshualitt8059eb92014-12-29 15:10:07 -08001087 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*ellipseXRadius +
1088 viewMatrix[SkMatrix::kMSkewY]*ellipseYRadius);
1089 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*ellipseXRadius +
1090 viewMatrix[SkMatrix::kMScaleY]*ellipseYRadius);
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001091
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001092 // do (potentially) anisotropic mapping of stroke
1093 SkVector scaledStroke;
1094 SkScalar strokeWidth = stroke.getWidth();
joshualitt8059eb92014-12-29 15:10:07 -08001095 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1096 viewMatrix[SkMatrix::kMSkewY]));
1097 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1098 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001099
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001100 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001101 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1102 SkStrokeRec::kHairline_Style == style;
1103 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001104
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001105 SkScalar innerXRadius = 0;
1106 SkScalar innerYRadius = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001107 if (hasStroke) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001108 if (SkScalarNearlyZero(scaledStroke.length())) {
1109 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1110 } else {
1111 scaledStroke.scale(SK_ScalarHalf);
1112 }
1113
1114 // we only handle thick strokes for near-circular ellipses
1115 if (scaledStroke.length() > SK_ScalarHalf &&
1116 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
joshualitt3e708c52015-04-30 13:49:27 -07001117 return NULL;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001118 }
1119
1120 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1121 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
1122 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
joshualitt3e708c52015-04-30 13:49:27 -07001123 return NULL;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001124 }
1125
1126 // this is legit only if scale & translation (which should be the case at the moment)
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001127 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001128 innerXRadius = xRadius - scaledStroke.fX;
1129 innerYRadius = yRadius - scaledStroke.fY;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001130 }
1131
1132 xRadius += scaledStroke.fX;
1133 yRadius += scaledStroke.fY;
1134 }
1135
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001136 // We've extended the outer x radius out half a pixel to antialias.
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +00001137 // This will also expand the rect so all the pixels will be captured.
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001138 // TODO: Consider if we should use sqrt(2)/2 instead
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +00001139 xRadius += SK_ScalarHalf;
1140 yRadius += SK_ScalarHalf;
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001141
joshualitt76e7fb62015-02-11 08:52:27 -08001142 EllipseBatch::Geometry geometry;
1143 geometry.fViewMatrix = viewMatrix;
1144 geometry.fColor = color;
1145 geometry.fXRadius = xRadius;
1146 geometry.fYRadius = yRadius;
1147 geometry.fInnerXRadius = innerXRadius;
1148 geometry.fInnerYRadius = innerYRadius;
1149 geometry.fStroke = isStrokeOnly && innerXRadius > 0 && innerYRadius > 0;
joshualittd96a67b2015-05-05 14:09:05 -07001150 geometry.fDevBounds = SkRect::MakeLTRB(center.fX - xRadius, center.fY - yRadius,
1151 center.fX + xRadius, center.fY + yRadius);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +00001152
joshualitt3e708c52015-04-30 13:49:27 -07001153 return EllipseBatch::Create(geometry);
1154}
1155
1156bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
1157 GrPipelineBuilder* pipelineBuilder,
1158 GrColor color,
1159 const SkMatrix& viewMatrix,
1160 bool useCoverageAA,
1161 const SkRect& ellipse,
1162 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07001163 SkAutoTUnref<GrBatch> batch(create_ellipse_batch(color, viewMatrix, useCoverageAA, ellipse,
joshualittd96a67b2015-05-05 14:09:05 -07001164 stroke));
joshualitt3e708c52015-04-30 13:49:27 -07001165 if (!batch) {
1166 return false;
1167 }
1168
joshualitt99c7c072015-05-01 13:43:30 -07001169 target->drawBatch(pipelineBuilder, batch);
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +00001170 return true;
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001171}
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001172
joshualitt76e7fb62015-02-11 08:52:27 -08001173/////////////////////////////////////////////////////////////////////////////////////////////////
1174
1175class DIEllipseBatch : public GrBatch {
1176public:
1177 struct Geometry {
1178 GrColor fColor;
1179 SkMatrix fViewMatrix;
1180 SkScalar fXRadius;
1181 SkScalar fYRadius;
1182 SkScalar fInnerXRadius;
1183 SkScalar fInnerYRadius;
1184 SkScalar fGeoDx;
1185 SkScalar fGeoDy;
1186 DIEllipseEdgeEffect::Mode fMode;
egdaniel9ef1bb12015-04-20 12:28:57 -07001187 SkRect fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001188 };
1189
joshualitt99c7c072015-05-01 13:43:30 -07001190 static GrBatch* Create(const Geometry& geometry, const SkRect& bounds) {
1191 return SkNEW_ARGS(DIEllipseBatch, (geometry, bounds));
joshualitt76e7fb62015-02-11 08:52:27 -08001192 }
1193
mtklein36352bf2015-03-25 18:17:31 -07001194 const char* name() const override { return "DIEllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001195
mtklein36352bf2015-03-25 18:17:31 -07001196 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001197 // When this is called on a batch, there is only one geometry bundle
1198 out->setKnownFourComponents(fGeoData[0].fColor);
1199 }
mtklein36352bf2015-03-25 18:17:31 -07001200 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001201 out->setUnknownSingleComponent();
1202 }
1203
mtklein36352bf2015-03-25 18:17:31 -07001204 void initBatchTracker(const GrPipelineInfo& init) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001205 // Handle any color overrides
1206 if (init.fColorIgnored) {
1207 fGeoData[0].fColor = GrColor_ILLEGAL;
1208 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
1209 fGeoData[0].fColor = init.fOverrideColor;
1210 }
1211
1212 // setup batch properties
1213 fBatch.fColorIgnored = init.fColorIgnored;
1214 fBatch.fColor = fGeoData[0].fColor;
1215 fBatch.fMode = fGeoData[0].fMode;
1216 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
1217 fBatch.fCoverageIgnored = init.fCoverageIgnored;
1218 }
1219
mtklein36352bf2015-03-25 18:17:31 -07001220 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001221 // Setup geometry processor
1222 SkAutoTUnref<GrGeometryProcessor> gp(DIEllipseEdgeEffect::Create(this->color(),
1223 this->viewMatrix(),
1224 this->mode()));
1225
joshualitt76e7fb62015-02-11 08:52:27 -08001226 batchTarget->initDraw(gp, pipeline);
1227
1228 // TODO this is hacky, but the only way we have to initialize the GP is to use the
1229 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
1230 // everywhere we can remove this nastiness
1231 GrPipelineInfo init;
1232 init.fColorIgnored = fBatch.fColorIgnored;
1233 init.fOverrideColor = GrColor_ILLEGAL;
1234 init.fCoverageIgnored = fBatch.fCoverageIgnored;
1235 init.fUsesLocalCoords = this->usesLocalCoords();
1236 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
1237
1238 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001239 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001240 SkASSERT(vertexStride == sizeof(DIEllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -07001241 QuadHelper helper;
1242 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
1243 helper.init(batchTarget, vertexStride, instanceCount));
1244 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001245 return;
1246 }
1247
joshualitt76e7fb62015-02-11 08:52:27 -08001248 for (int i = 0; i < instanceCount; i++) {
bsalomonb5238a72015-05-05 07:49:49 -07001249 Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001250
bsalomonb5238a72015-05-05 07:49:49 -07001251 SkScalar xRadius = geom.fXRadius;
1252 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001253
bsalomonb5238a72015-05-05 07:49:49 -07001254 const SkRect& bounds = geom.fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001255
1256 // This adjusts the "radius" to include the half-pixel border
bsalomonb5238a72015-05-05 07:49:49 -07001257 SkScalar offsetDx = SkScalarDiv(geom.fGeoDx, xRadius);
1258 SkScalar offsetDy = SkScalarDiv(geom.fGeoDy, yRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001259
bsalomonb5238a72015-05-05 07:49:49 -07001260 SkScalar innerRatioX = SkScalarDiv(xRadius, geom.fInnerXRadius);
1261 SkScalar innerRatioY = SkScalarDiv(yRadius, geom.fInnerYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001262
1263 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
1264 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
1265 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
1266
1267 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
1268 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
1269 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
1270
1271 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
1272 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1273 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
1274
1275 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
1276 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1277 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
1278
bsalomonb5238a72015-05-05 07:49:49 -07001279 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001280 }
bsalomonb5238a72015-05-05 07:49:49 -07001281 helper.issueDraws(batchTarget);
joshualitt76e7fb62015-02-11 08:52:27 -08001282 }
1283
1284 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1285
1286private:
joshualitt99c7c072015-05-01 13:43:30 -07001287 DIEllipseBatch(const Geometry& geometry, const SkRect& bounds) {
joshualitt76e7fb62015-02-11 08:52:27 -08001288 this->initClassID<DIEllipseBatch>();
1289 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -07001290
1291 this->setBounds(bounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001292 }
1293
mtklein36352bf2015-03-25 18:17:31 -07001294 bool onCombineIfPossible(GrBatch* t) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001295 DIEllipseBatch* that = t->cast<DIEllipseBatch>();
1296
1297 // TODO use vertex color to avoid breaking batches
1298 if (this->color() != that->color()) {
1299 return false;
1300 }
1301
1302 if (this->mode() != that->mode()) {
1303 return false;
1304 }
1305
joshualittd96a67b2015-05-05 14:09:05 -07001306 // TODO rewrite to allow positioning on CPU
1307 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
joshualitt76e7fb62015-02-11 08:52:27 -08001308 return false;
1309 }
1310
1311 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -07001312 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -08001313 return true;
1314 }
1315
1316 GrColor color() const { return fBatch.fColor; }
1317 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1318 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1319 DIEllipseEdgeEffect::Mode mode() const { return fBatch.fMode; }
1320
1321 struct BatchTracker {
1322 GrColor fColor;
1323 DIEllipseEdgeEffect::Mode fMode;
1324 bool fUsesLocalCoords;
1325 bool fColorIgnored;
1326 bool fCoverageIgnored;
1327 };
1328
joshualitt76e7fb62015-02-11 08:52:27 -08001329 BatchTracker fBatch;
1330 SkSTArray<1, Geometry, true> fGeoData;
1331};
1332
joshualitt3e708c52015-04-30 13:49:27 -07001333static GrBatch* create_diellipse_batch(GrColor color,
1334 const SkMatrix& viewMatrix,
1335 bool useCoverageAA,
1336 const SkRect& ellipse,
joshualittd96a67b2015-05-05 14:09:05 -07001337 const SkStrokeRec& stroke) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +00001338 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001339 SkScalar xRadius = SkScalarHalf(ellipse.width());
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +00001340 SkScalar yRadius = SkScalarHalf(ellipse.height());
1341
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001342 SkStrokeRec::Style style = stroke.getStyle();
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +00001343 DIEllipseEdgeEffect::Mode mode = (SkStrokeRec::kStroke_Style == style) ?
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001344 DIEllipseEdgeEffect::kStroke :
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +00001345 (SkStrokeRec::kHairline_Style == style) ?
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001346 DIEllipseEdgeEffect::kHairline : DIEllipseEdgeEffect::kFill;
1347
1348 SkScalar innerXRadius = 0;
1349 SkScalar innerYRadius = 0;
1350 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
1351 SkScalar strokeWidth = stroke.getWidth();
1352
1353 if (SkScalarNearlyZero(strokeWidth)) {
1354 strokeWidth = SK_ScalarHalf;
1355 } else {
1356 strokeWidth *= SK_ScalarHalf;
1357 }
1358
1359 // we only handle thick strokes for near-circular ellipses
1360 if (strokeWidth > SK_ScalarHalf &&
1361 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
joshualitt3e708c52015-04-30 13:49:27 -07001362 return NULL;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001363 }
1364
1365 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1366 if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius ||
1367 strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) {
joshualitt3e708c52015-04-30 13:49:27 -07001368 return NULL;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001369 }
1370
1371 // set inner radius (if needed)
1372 if (SkStrokeRec::kStroke_Style == style) {
1373 innerXRadius = xRadius - strokeWidth;
1374 innerYRadius = yRadius - strokeWidth;
1375 }
1376
1377 xRadius += strokeWidth;
1378 yRadius += strokeWidth;
1379 }
1380 if (DIEllipseEdgeEffect::kStroke == mode) {
1381 mode = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseEdgeEffect::kStroke :
1382 DIEllipseEdgeEffect::kFill;
1383 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001384
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001385 // This expands the outer rect so that after CTM we end up with a half-pixel border
joshualitt8059eb92014-12-29 15:10:07 -08001386 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
1387 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
1388 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
1389 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001390 SkScalar geoDx = SkScalarDiv(SK_ScalarHalf, SkScalarSqrt(a*a + c*c));
1391 SkScalar geoDy = SkScalarDiv(SK_ScalarHalf, SkScalarSqrt(b*b + d*d));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001392
joshualitt76e7fb62015-02-11 08:52:27 -08001393 DIEllipseBatch::Geometry geometry;
1394 geometry.fViewMatrix = viewMatrix;
1395 geometry.fColor = color;
1396 geometry.fXRadius = xRadius;
1397 geometry.fYRadius = yRadius;
1398 geometry.fInnerXRadius = innerXRadius;
1399 geometry.fInnerYRadius = innerYRadius;
1400 geometry.fGeoDx = geoDx;
1401 geometry.fGeoDy = geoDy;
1402 geometry.fMode = mode;
joshualittd96a67b2015-05-05 14:09:05 -07001403 geometry.fBounds = SkRect::MakeLTRB(center.fX - xRadius - geoDx, center.fY - yRadius - geoDy,
1404 center.fX + xRadius + geoDx, center.fY + yRadius + geoDy);
egdaniel9ef1bb12015-04-20 12:28:57 -07001405
joshualittd96a67b2015-05-05 14:09:05 -07001406 SkRect devBounds = geometry.fBounds;
1407 viewMatrix.mapRect(&devBounds);
1408 return DIEllipseBatch::Create(geometry, devBounds);
joshualitt3e708c52015-04-30 13:49:27 -07001409}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001410
joshualitt3e708c52015-04-30 13:49:27 -07001411bool GrOvalRenderer::drawDIEllipse(GrDrawTarget* target,
1412 GrPipelineBuilder* pipelineBuilder,
1413 GrColor color,
1414 const SkMatrix& viewMatrix,
1415 bool useCoverageAA,
1416 const SkRect& ellipse,
1417 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07001418 SkAutoTUnref<GrBatch> batch(create_diellipse_batch(color, viewMatrix, useCoverageAA, ellipse,
joshualittd96a67b2015-05-05 14:09:05 -07001419 stroke));
joshualitt3e708c52015-04-30 13:49:27 -07001420 if (!batch) {
1421 return false;
1422 }
joshualitt99c7c072015-05-01 13:43:30 -07001423 target->drawBatch(pipelineBuilder, batch);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001424 return true;
1425}
1426
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001427///////////////////////////////////////////////////////////////////////////////
1428
1429static const uint16_t gRRectIndices[] = {
1430 // corners
1431 0, 1, 5, 0, 5, 4,
1432 2, 3, 7, 2, 7, 6,
1433 8, 9, 13, 8, 13, 12,
1434 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001435
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001436 // edges
1437 1, 2, 6, 1, 6, 5,
1438 4, 5, 9, 4, 9, 8,
1439 6, 7, 11, 6, 11, 10,
1440 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001441
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001442 // center
1443 // we place this at the end so that we can ignore these indices when rendering stroke-only
1444 5, 6, 10, 5, 10, 9
1445};
1446
joshualitt5ead6da2014-10-22 16:00:29 -07001447static const int kIndicesPerStrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
1448static const int kIndicesPerRRect = SK_ARRAY_COUNT(gRRectIndices);
1449static const int kVertsPerRRect = 16;
1450static const int kNumRRectsInIndexBuffer = 256;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001451
bsalomoned0bcad2015-05-04 10:36:42 -07001452GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1453GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
1454static const GrIndexBuffer* ref_rrect_index_buffer(bool strokeOnly,
1455 GrResourceProvider* resourceProvider) {
1456 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1457 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
1458 if (strokeOnly) {
1459 return resourceProvider->refOrCreateInstancedIndexBuffer(
1460 gRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
1461 gStrokeRRectOnlyIndexBufferKey);
1462 } else {
1463 return resourceProvider->refOrCreateInstancedIndexBuffer(
1464 gRRectIndices, kIndicesPerRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
1465 gRRectOnlyIndexBufferKey);
1466
1467 }
1468}
1469
joshualitt9853cce2014-11-17 14:22:48 -08001470bool GrOvalRenderer::drawDRRect(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -08001471 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -08001472 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -08001473 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -08001474 bool useAA,
1475 const SkRRect& origOuter,
1476 const SkRRect& origInner) {
bsalomon8af05232014-06-03 06:34:58 -07001477 bool applyAA = useAA &&
egdaniel0bdeec92015-02-23 12:12:54 -08001478 !pipelineBuilder->getRenderTarget()->isMultisampled();
bsalomon6be6f7c2015-02-26 13:05:21 -08001479 GrPipelineBuilder::AutoRestoreFragmentProcessors arfp;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001480 if (!origInner.isEmpty()) {
1481 SkTCopyOnFirstWrite<SkRRect> inner(origInner);
joshualitt8059eb92014-12-29 15:10:07 -08001482 if (!viewMatrix.isIdentity()) {
1483 if (!origInner.transform(viewMatrix, inner.writable())) {
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001484 return false;
1485 }
1486 }
joshualittb0a8a372014-09-23 09:50:21 -07001487 GrPrimitiveEdgeType edgeType = applyAA ?
1488 kInverseFillAA_GrProcessorEdgeType :
1489 kInverseFillBW_GrProcessorEdgeType;
joshualitt2e3b3e32014-12-09 13:31:14 -08001490 // TODO this needs to be a geometry processor
joshualittb0a8a372014-09-23 09:50:21 -07001491 GrFragmentProcessor* fp = GrRRectEffect::Create(edgeType, *inner);
1492 if (NULL == fp) {
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001493 return false;
1494 }
bsalomon6be6f7c2015-02-26 13:05:21 -08001495 arfp.set(pipelineBuilder);
egdaniel8dd688b2015-01-22 10:16:09 -08001496 pipelineBuilder->addCoverageProcessor(fp)->unref();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001497 }
1498
1499 SkStrokeRec fillRec(SkStrokeRec::kFill_InitStyle);
egdaniel8dd688b2015-01-22 10:16:09 -08001500 if (this->drawRRect(target, pipelineBuilder, color, viewMatrix, useAA, origOuter, fillRec)) {
bsalomon8af05232014-06-03 06:34:58 -07001501 return true;
1502 }
1503
1504 SkASSERT(!origOuter.isEmpty());
1505 SkTCopyOnFirstWrite<SkRRect> outer(origOuter);
joshualitt8059eb92014-12-29 15:10:07 -08001506 if (!viewMatrix.isIdentity()) {
1507 if (!origOuter.transform(viewMatrix, outer.writable())) {
bsalomon8af05232014-06-03 06:34:58 -07001508 return false;
1509 }
1510 }
joshualittb0a8a372014-09-23 09:50:21 -07001511 GrPrimitiveEdgeType edgeType = applyAA ? kFillAA_GrProcessorEdgeType :
joshualitt76e7fb62015-02-11 08:52:27 -08001512 kFillBW_GrProcessorEdgeType;
joshualittb0a8a372014-09-23 09:50:21 -07001513 GrFragmentProcessor* effect = GrRRectEffect::Create(edgeType, *outer);
bsalomon8af05232014-06-03 06:34:58 -07001514 if (NULL == effect) {
1515 return false;
1516 }
bsalomon6be6f7c2015-02-26 13:05:21 -08001517 if (!arfp.isSet()) {
1518 arfp.set(pipelineBuilder);
bsalomon8af05232014-06-03 06:34:58 -07001519 }
joshualittd27f73e2014-12-29 07:43:36 -08001520
1521 SkMatrix invert;
joshualitt8059eb92014-12-29 15:10:07 -08001522 if (!viewMatrix.invert(&invert)) {
bsalomon8af05232014-06-03 06:34:58 -07001523 return false;
1524 }
joshualittd27f73e2014-12-29 07:43:36 -08001525
egdaniel8dd688b2015-01-22 10:16:09 -08001526 pipelineBuilder->addCoverageProcessor(effect)->unref();
bsalomon8af05232014-06-03 06:34:58 -07001527 SkRect bounds = outer->getBounds();
1528 if (applyAA) {
1529 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1530 }
egdaniel8dd688b2015-01-22 10:16:09 -08001531 target->drawRect(pipelineBuilder, color, SkMatrix::I(), bounds, NULL, &invert);
bsalomon8af05232014-06-03 06:34:58 -07001532 return true;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001533}
1534
joshualitt76e7fb62015-02-11 08:52:27 -08001535///////////////////////////////////////////////////////////////////////////////////////////////////
1536
1537class RRectCircleRendererBatch : public GrBatch {
1538public:
1539 struct Geometry {
1540 GrColor fColor;
1541 SkMatrix fViewMatrix;
1542 SkScalar fInnerRadius;
1543 SkScalar fOuterRadius;
1544 bool fStroke;
egdanielbc227142015-04-21 06:28:08 -07001545 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001546 };
1547
bsalomoned0bcad2015-05-04 10:36:42 -07001548 static GrBatch* Create(const Geometry& geometry) {
1549 return SkNEW_ARGS(RRectCircleRendererBatch, (geometry));
joshualitt76e7fb62015-02-11 08:52:27 -08001550 }
1551
mtklein36352bf2015-03-25 18:17:31 -07001552 const char* name() const override { return "RRectCircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001553
mtklein36352bf2015-03-25 18:17:31 -07001554 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001555 // When this is called on a batch, there is only one geometry bundle
1556 out->setKnownFourComponents(fGeoData[0].fColor);
1557 }
mtklein36352bf2015-03-25 18:17:31 -07001558 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001559 out->setUnknownSingleComponent();
1560 }
1561
mtklein36352bf2015-03-25 18:17:31 -07001562 void initBatchTracker(const GrPipelineInfo& init) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001563 // Handle any color overrides
1564 if (init.fColorIgnored) {
1565 fGeoData[0].fColor = GrColor_ILLEGAL;
1566 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
1567 fGeoData[0].fColor = init.fOverrideColor;
1568 }
1569
1570 // setup batch properties
1571 fBatch.fColorIgnored = init.fColorIgnored;
1572 fBatch.fColor = fGeoData[0].fColor;
1573 fBatch.fStroke = fGeoData[0].fStroke;
1574 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
1575 fBatch.fCoverageIgnored = init.fCoverageIgnored;
1576 }
1577
mtklein36352bf2015-03-25 18:17:31 -07001578 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001579 // reset to device coordinates
1580 SkMatrix invert;
1581 if (!this->viewMatrix().invert(&invert)) {
1582 SkDebugf("Failed to invert\n");
1583 return;
1584 }
1585
1586 // Setup geometry processor
1587 SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(),
1588 this->stroke(),
1589 invert));
1590
1591 batchTarget->initDraw(gp, pipeline);
1592
1593 // TODO this is hacky, but the only way we have to initialize the GP is to use the
1594 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
1595 // everywhere we can remove this nastiness
1596 GrPipelineInfo init;
1597 init.fColorIgnored = fBatch.fColorIgnored;
1598 init.fOverrideColor = GrColor_ILLEGAL;
1599 init.fCoverageIgnored = fBatch.fCoverageIgnored;
1600 init.fUsesLocalCoords = this->usesLocalCoords();
1601 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
1602
1603 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001604 size_t vertexStride = gp->getVertexStride();
1605 SkASSERT(vertexStride == sizeof(CircleVertex));
1606
bsalomonb5238a72015-05-05 07:49:49 -07001607 // drop out the middle quad if we're stroked
1608 int indicesPerInstance = this->stroke() ? kIndicesPerStrokeRRect : kIndicesPerRRect;
bsalomoned0bcad2015-05-04 10:36:42 -07001609 SkAutoTUnref<const GrIndexBuffer> indexBuffer(
1610 ref_rrect_index_buffer(this->stroke(), batchTarget->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08001611
bsalomonb5238a72015-05-05 07:49:49 -07001612 InstancedHelper helper;
1613 CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(batchTarget,
1614 kTriangles_GrPrimitiveType, vertexStride, indexBuffer, kVertsPerRRect,
1615 indicesPerInstance, instanceCount));
1616 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08001617 SkDebugf("Could not allocate vertices\n");
1618 return;
1619 }
1620
joshualitt76e7fb62015-02-11 08:52:27 -08001621 for (int i = 0; i < instanceCount; i++) {
1622 Geometry& args = fGeoData[i];
1623
1624 SkScalar outerRadius = args.fOuterRadius;
1625
egdanielbc227142015-04-21 06:28:08 -07001626 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001627
1628 SkScalar yCoords[4] = {
1629 bounds.fTop,
1630 bounds.fTop + outerRadius,
1631 bounds.fBottom - outerRadius,
1632 bounds.fBottom
1633 };
1634
1635 SkScalar yOuterRadii[4] = {-1, 0, 0, 1 };
1636 // The inner radius in the vertex data must be specified in normalized space.
1637 SkScalar innerRadius = args.fInnerRadius / args.fOuterRadius;
1638 for (int i = 0; i < 4; ++i) {
1639 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1640 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1641 verts->fOuterRadius = outerRadius;
1642 verts->fInnerRadius = innerRadius;
1643 verts++;
1644
1645 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
1646 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1647 verts->fOuterRadius = outerRadius;
1648 verts->fInnerRadius = innerRadius;
1649 verts++;
1650
1651 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
1652 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1653 verts->fOuterRadius = outerRadius;
1654 verts->fInnerRadius = innerRadius;
1655 verts++;
1656
1657 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1658 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1659 verts->fOuterRadius = outerRadius;
1660 verts->fInnerRadius = innerRadius;
1661 verts++;
1662 }
1663 }
1664
bsalomonb5238a72015-05-05 07:49:49 -07001665 helper.issueDraws(batchTarget);
joshualitt76e7fb62015-02-11 08:52:27 -08001666 }
1667
1668 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1669
1670private:
bsalomoned0bcad2015-05-04 10:36:42 -07001671 RRectCircleRendererBatch(const Geometry& geometry) {
joshualitt76e7fb62015-02-11 08:52:27 -08001672 this->initClassID<RRectCircleRendererBatch>();
1673 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -07001674
1675 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001676 }
1677
mtklein36352bf2015-03-25 18:17:31 -07001678 bool onCombineIfPossible(GrBatch* t) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001679 RRectCircleRendererBatch* that = t->cast<RRectCircleRendererBatch>();
1680
1681 // TODO use vertex color to avoid breaking batches
1682 if (this->color() != that->color()) {
1683 return false;
1684 }
1685
1686 if (this->stroke() != that->stroke()) {
1687 return false;
1688 }
1689
1690 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1691 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1692 return false;
1693 }
1694
1695 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -07001696 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -08001697 return true;
1698 }
1699
1700 GrColor color() const { return fBatch.fColor; }
1701 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1702 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1703 bool stroke() const { return fBatch.fStroke; }
1704
1705 struct BatchTracker {
1706 GrColor fColor;
1707 bool fStroke;
1708 bool fUsesLocalCoords;
1709 bool fColorIgnored;
1710 bool fCoverageIgnored;
1711 };
1712
joshualitt76e7fb62015-02-11 08:52:27 -08001713 BatchTracker fBatch;
1714 SkSTArray<1, Geometry, true> fGeoData;
joshualitt76e7fb62015-02-11 08:52:27 -08001715};
1716
1717class RRectEllipseRendererBatch : public GrBatch {
1718public:
1719 struct Geometry {
1720 GrColor fColor;
1721 SkMatrix fViewMatrix;
1722 SkScalar fXRadius;
1723 SkScalar fYRadius;
1724 SkScalar fInnerXRadius;
1725 SkScalar fInnerYRadius;
1726 bool fStroke;
egdanielbc227142015-04-21 06:28:08 -07001727 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001728 };
1729
bsalomoned0bcad2015-05-04 10:36:42 -07001730 static GrBatch* Create(const Geometry& geometry) {
1731 return SkNEW_ARGS(RRectEllipseRendererBatch, (geometry));
joshualitt76e7fb62015-02-11 08:52:27 -08001732 }
1733
mtklein36352bf2015-03-25 18:17:31 -07001734 const char* name() const override { return "RRectEllipseRendererBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001735
mtklein36352bf2015-03-25 18:17:31 -07001736 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001737 // When this is called on a batch, there is only one geometry bundle
1738 out->setKnownFourComponents(fGeoData[0].fColor);
1739 }
mtklein36352bf2015-03-25 18:17:31 -07001740 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001741 out->setUnknownSingleComponent();
1742 }
1743
mtklein36352bf2015-03-25 18:17:31 -07001744 void initBatchTracker(const GrPipelineInfo& init) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001745 // Handle any color overrides
1746 if (init.fColorIgnored) {
1747 fGeoData[0].fColor = GrColor_ILLEGAL;
1748 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
1749 fGeoData[0].fColor = init.fOverrideColor;
1750 }
1751
1752 // setup batch properties
1753 fBatch.fColorIgnored = init.fColorIgnored;
1754 fBatch.fColor = fGeoData[0].fColor;
1755 fBatch.fStroke = fGeoData[0].fStroke;
1756 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
1757 fBatch.fCoverageIgnored = init.fCoverageIgnored;
1758 }
1759
mtklein36352bf2015-03-25 18:17:31 -07001760 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001761 // reset to device coordinates
1762 SkMatrix invert;
1763 if (!this->viewMatrix().invert(&invert)) {
1764 SkDebugf("Failed to invert\n");
1765 return;
1766 }
1767
1768 // Setup geometry processor
1769 SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(),
1770 this->stroke(),
1771 invert));
1772
1773 batchTarget->initDraw(gp, pipeline);
1774
1775 // TODO this is hacky, but the only way we have to initialize the GP is to use the
1776 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
1777 // everywhere we can remove this nastiness
1778 GrPipelineInfo init;
1779 init.fColorIgnored = fBatch.fColorIgnored;
1780 init.fOverrideColor = GrColor_ILLEGAL;
1781 init.fCoverageIgnored = fBatch.fCoverageIgnored;
1782 init.fUsesLocalCoords = this->usesLocalCoords();
1783 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
1784
1785 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001786 size_t vertexStride = gp->getVertexStride();
1787 SkASSERT(vertexStride == sizeof(EllipseVertex));
1788
bsalomonb5238a72015-05-05 07:49:49 -07001789 // drop out the middle quad if we're stroked
1790 int indicesPerInstance = this->stroke() ? kIndicesPerStrokeRRect : kIndicesPerRRect;
bsalomoned0bcad2015-05-04 10:36:42 -07001791 SkAutoTUnref<const GrIndexBuffer> indexBuffer(
1792 ref_rrect_index_buffer(this->stroke(), batchTarget->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08001793
bsalomonb5238a72015-05-05 07:49:49 -07001794 InstancedHelper helper;
1795 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
1796 helper.init(batchTarget, kTriangles_GrPrimitiveType, vertexStride, indexBuffer,
1797 kVertsPerRRect, indicesPerInstance, instanceCount));
1798 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08001799 SkDebugf("Could not allocate vertices\n");
1800 return;
1801 }
1802
joshualitt76e7fb62015-02-11 08:52:27 -08001803 for (int i = 0; i < instanceCount; i++) {
1804 Geometry& args = fGeoData[i];
1805
1806 // Compute the reciprocals of the radii here to save time in the shader
1807 SkScalar xRadRecip = SkScalarInvert(args.fXRadius);
1808 SkScalar yRadRecip = SkScalarInvert(args.fYRadius);
1809 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
1810 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
1811
1812 // Extend the radii out half a pixel to antialias.
1813 SkScalar xOuterRadius = args.fXRadius + SK_ScalarHalf;
1814 SkScalar yOuterRadius = args.fYRadius + SK_ScalarHalf;
1815
egdanielbc227142015-04-21 06:28:08 -07001816 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001817
1818 SkScalar yCoords[4] = {
1819 bounds.fTop,
1820 bounds.fTop + yOuterRadius,
1821 bounds.fBottom - yOuterRadius,
1822 bounds.fBottom
1823 };
1824 SkScalar yOuterOffsets[4] = {
1825 yOuterRadius,
1826 SK_ScalarNearlyZero, // we're using inversesqrt() in shader, so can't be exactly 0
1827 SK_ScalarNearlyZero,
1828 yOuterRadius
1829 };
1830
1831 for (int i = 0; i < 4; ++i) {
1832 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1833 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1834 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1835 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1836 verts++;
1837
1838 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
1839 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1840 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1841 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1842 verts++;
1843
1844 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
1845 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1846 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1847 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1848 verts++;
1849
1850 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1851 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1852 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1853 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1854 verts++;
1855 }
1856 }
bsalomonb5238a72015-05-05 07:49:49 -07001857 helper.issueDraws(batchTarget);
joshualitt76e7fb62015-02-11 08:52:27 -08001858 }
1859
1860 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1861
1862private:
bsalomoned0bcad2015-05-04 10:36:42 -07001863 RRectEllipseRendererBatch(const Geometry& geometry) {
joshualitt76e7fb62015-02-11 08:52:27 -08001864 this->initClassID<RRectEllipseRendererBatch>();
1865 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -07001866
1867 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001868 }
1869
mtklein36352bf2015-03-25 18:17:31 -07001870 bool onCombineIfPossible(GrBatch* t) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001871 RRectEllipseRendererBatch* that = t->cast<RRectEllipseRendererBatch>();
1872
1873 // TODO use vertex color to avoid breaking batches
1874 if (this->color() != that->color()) {
1875 return false;
1876 }
1877
1878 if (this->stroke() != that->stroke()) {
1879 return false;
1880 }
1881
1882 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1883 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1884 return false;
1885 }
1886
1887 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -07001888 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -08001889 return true;
1890 }
1891
1892 GrColor color() const { return fBatch.fColor; }
1893 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1894 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1895 bool stroke() const { return fBatch.fStroke; }
1896
1897 struct BatchTracker {
1898 GrColor fColor;
1899 bool fStroke;
1900 bool fUsesLocalCoords;
1901 bool fColorIgnored;
1902 bool fCoverageIgnored;
1903 };
1904
joshualitt76e7fb62015-02-11 08:52:27 -08001905 BatchTracker fBatch;
1906 SkSTArray<1, Geometry, true> fGeoData;
joshualitt76e7fb62015-02-11 08:52:27 -08001907};
1908
joshualitt3e708c52015-04-30 13:49:27 -07001909static GrBatch* create_rrect_batch(GrColor color,
1910 const SkMatrix& viewMatrix,
1911 const SkRRect& rrect,
joshualittd96a67b2015-05-05 14:09:05 -07001912 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07001913 SkASSERT(viewMatrix.rectStaysRect());
1914 SkASSERT(rrect.isSimple());
1915 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00001916
joshualitt3e708c52015-04-30 13:49:27 -07001917 // RRect batchs only handle simple, but not too simple, rrects
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001918 // do any matrix crunching before we reset the draw state for device coords
1919 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07001920 SkRect bounds;
1921 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001922
1923 SkVector radii = rrect.getSimpleRadii();
joshualitt8059eb92014-12-29 15:10:07 -08001924 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*radii.fX +
1925 viewMatrix[SkMatrix::kMSkewY]*radii.fY);
1926 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*radii.fX +
1927 viewMatrix[SkMatrix::kMScaleY]*radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001928
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001929 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001930
1931 // do (potentially) anisotropic mapping of stroke
1932 SkVector scaledStroke;
1933 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001934
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001935 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1936 SkStrokeRec::kHairline_Style == style;
1937 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1938
1939 if (hasStroke) {
1940 if (SkStrokeRec::kHairline_Style == style) {
1941 scaledStroke.set(1, 1);
1942 } else {
joshualitt8059eb92014-12-29 15:10:07 -08001943 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1944 viewMatrix[SkMatrix::kMSkewY]));
1945 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1946 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001947 }
1948
1949 // if half of strokewidth is greater than radius, we don't handle that right now
1950 if (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius) {
joshualitt3e708c52015-04-30 13:49:27 -07001951 return NULL;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001952 }
1953 }
1954
1955 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
1956 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
1957 // patch will have fractional coverage. This only matters when the interior is actually filled.
1958 // We could consider falling back to rect rendering here, since a tiny radius is
1959 // indistinguishable from a square corner.
1960 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
joshualitt3e708c52015-04-30 13:49:27 -07001961 return NULL;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001962 }
1963
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001964 // if the corners are circles, use the circle renderer
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001965 if ((!hasStroke || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001966 SkScalar innerRadius = 0.0f;
1967 SkScalar outerRadius = xRadius;
1968 SkScalar halfWidth = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001969 if (hasStroke) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001970 if (SkScalarNearlyZero(scaledStroke.fX)) {
1971 halfWidth = SK_ScalarHalf;
1972 } else {
1973 halfWidth = SkScalarHalf(scaledStroke.fX);
1974 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001975
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001976 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001977 innerRadius = xRadius - halfWidth;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001978 }
1979 outerRadius += halfWidth;
joshualittd96a67b2015-05-05 14:09:05 -07001980 bounds.outset(halfWidth, halfWidth);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001981 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001982
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001983 isStrokeOnly = (isStrokeOnly && innerRadius >= 0);
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +00001984
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001985 // The radii are outset for two reasons. First, it allows the shader to simply perform
bsalomonce1c8862014-12-15 07:11:22 -08001986 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1987 // Second, the outer radius is used to compute the verts of the bounding box that is
1988 // rendered and the outset ensures the box will cover all partially covered by the rrect
1989 // corners.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001990 outerRadius += SK_ScalarHalf;
1991 innerRadius -= SK_ScalarHalf;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001992
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001993 // Expand the rect so all the pixels will be captured.
joshualittd96a67b2015-05-05 14:09:05 -07001994 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001995
joshualitt76e7fb62015-02-11 08:52:27 -08001996 RRectCircleRendererBatch::Geometry geometry;
1997 geometry.fViewMatrix = viewMatrix;
1998 geometry.fColor = color;
1999 geometry.fInnerRadius = innerRadius;
2000 geometry.fOuterRadius = outerRadius;
2001 geometry.fStroke = isStrokeOnly;
joshualittd96a67b2015-05-05 14:09:05 -07002002 geometry.fDevBounds = bounds;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002003
bsalomoned0bcad2015-05-04 10:36:42 -07002004 return RRectCircleRendererBatch::Create(geometry);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002005 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002006 } else {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002007 SkScalar innerXRadius = 0.0f;
2008 SkScalar innerYRadius = 0.0f;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002009 if (hasStroke) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002010 if (SkScalarNearlyZero(scaledStroke.length())) {
2011 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
2012 } else {
2013 scaledStroke.scale(SK_ScalarHalf);
2014 }
2015
2016 // we only handle thick strokes for near-circular ellipses
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +00002017 if (scaledStroke.length() > SK_ScalarHalf &&
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002018 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
joshualitt3e708c52015-04-30 13:49:27 -07002019 return NULL;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002020 }
2021
2022 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
2023 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
2024 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
joshualitt3e708c52015-04-30 13:49:27 -07002025 return NULL;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002026 }
2027
2028 // this is legit only if scale & translation (which should be the case at the moment)
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002029 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002030 innerXRadius = xRadius - scaledStroke.fX;
2031 innerYRadius = yRadius - scaledStroke.fY;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002032 }
2033
2034 xRadius += scaledStroke.fX;
2035 yRadius += scaledStroke.fY;
joshualittd96a67b2015-05-05 14:09:05 -07002036 bounds.outset(scaledStroke.fX, scaledStroke.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002037 }
jvanverth@google.come3647412013-05-08 15:31:43 +00002038
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002039 isStrokeOnly = (isStrokeOnly && innerXRadius >= 0 && innerYRadius >= 0);
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +00002040
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002041 // Expand the rect so all the pixels will be captured.
joshualittd96a67b2015-05-05 14:09:05 -07002042 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002043
joshualitt76e7fb62015-02-11 08:52:27 -08002044 RRectEllipseRendererBatch::Geometry geometry;
2045 geometry.fViewMatrix = viewMatrix;
2046 geometry.fColor = color;
2047 geometry.fXRadius = xRadius;
2048 geometry.fYRadius = yRadius;
2049 geometry.fInnerXRadius = innerXRadius;
2050 geometry.fInnerYRadius = innerYRadius;
2051 geometry.fStroke = isStrokeOnly;
joshualittd96a67b2015-05-05 14:09:05 -07002052 geometry.fDevBounds = bounds;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002053
bsalomoned0bcad2015-05-04 10:36:42 -07002054 return RRectEllipseRendererBatch::Create(geometry);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002055 }
joshualitt3e708c52015-04-30 13:49:27 -07002056}
2057
2058bool GrOvalRenderer::drawRRect(GrDrawTarget* target,
2059 GrPipelineBuilder* pipelineBuilder,
2060 GrColor color,
2061 const SkMatrix& viewMatrix,
2062 bool useAA,
2063 const SkRRect& rrect,
2064 const SkStrokeRec& stroke) {
2065 if (rrect.isOval()) {
2066 return this->drawOval(target, pipelineBuilder, color, viewMatrix, useAA, rrect.getBounds(),
2067 stroke);
2068 }
2069
2070 bool useCoverageAA = useAA && !pipelineBuilder->getRenderTarget()->isMultisampled();
2071
2072 // only anti-aliased rrects for now
2073 if (!useCoverageAA) {
2074 return false;
2075 }
2076
2077 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
2078 return false;
2079 }
2080
joshualittd96a67b2015-05-05 14:09:05 -07002081 SkAutoTUnref<GrBatch> batch(create_rrect_batch(color, viewMatrix, rrect, stroke));
joshualitt3e708c52015-04-30 13:49:27 -07002082 if (!batch) {
2083 return false;
2084 }
2085
joshualitt99c7c072015-05-01 13:43:30 -07002086 target->drawBatch(pipelineBuilder, batch);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002087 return true;
2088}
joshualitt3e708c52015-04-30 13:49:27 -07002089
2090///////////////////////////////////////////////////////////////////////////////////////////////////
2091
2092#ifdef GR_TEST_UTILS
2093
2094static SkStrokeRec random_strokerec(SkRandom* random) {
2095 SkStrokeRec::InitStyle style =
2096 SkStrokeRec::InitStyle(random->nextULessThan(SkStrokeRec::kFill_InitStyle + 1));
2097 SkStrokeRec rec(style);
2098 bool strokeAndFill = random->nextBool();
2099 SkScalar strokeWidth = random->nextBool() ? 0.f : 1.f;
2100 rec.setStrokeStyle(strokeWidth, strokeAndFill);
2101 return rec;
2102}
2103
2104BATCH_TEST_DEFINE(CircleBatch) {
2105 SkMatrix viewMatrix = GrTest::TestMatrix(random);
2106 GrColor color = GrRandomColor(random);
2107 bool useCoverageAA = random->nextBool();
2108 SkRect circle = GrTest::TestRect(random);
joshualittd96a67b2015-05-05 14:09:05 -07002109 return create_circle_batch(color, viewMatrix, useCoverageAA, circle, random_strokerec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002110}
2111
2112BATCH_TEST_DEFINE(EllipseBatch) {
2113 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2114 GrColor color = GrRandomColor(random);
2115 bool useCoverageAA = random->nextBool();
2116 SkRect ellipse = GrTest::TestRect(random);
joshualitt3e708c52015-04-30 13:49:27 -07002117 return create_ellipse_batch(color, viewMatrix, useCoverageAA, ellipse,
joshualittd96a67b2015-05-05 14:09:05 -07002118 random_strokerec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002119}
2120
2121BATCH_TEST_DEFINE(DIEllipseBatch) {
2122 SkMatrix viewMatrix = GrTest::TestMatrix(random);
2123 GrColor color = GrRandomColor(random);
2124 bool useCoverageAA = random->nextBool();
2125 SkRect ellipse = GrTest::TestRect(random);
joshualitt3e708c52015-04-30 13:49:27 -07002126 return create_diellipse_batch(color, viewMatrix, useCoverageAA, ellipse,
joshualittd96a67b2015-05-05 14:09:05 -07002127 random_strokerec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002128}
2129
2130BATCH_TEST_DEFINE(RRectBatch) {
2131 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2132 GrColor color = GrRandomColor(random);
2133 const SkRRect& rrect = GrTest::TestRRectSimple(random);
joshualittd96a67b2015-05-05 14:09:05 -07002134 return create_rrect_batch(color, viewMatrix, rrect, random_strokerec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002135}
2136
2137#endif