blob: 8f078dbc4543aaae82c653616ed02f26554bc024 [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
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000010#include "GrEffect.h"
11#include "gl/GrGLEffect.h"
12#include "gl/GrGLSL.h"
commit-bot@chromium.org261dc562013-10-04 15:42:56 +000013#include "gl/GrGLVertexEffect.h"
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000014#include "GrTBackendEffectFactory.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000015
16#include "GrDrawState.h"
17#include "GrDrawTarget.h"
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000018#include "GrGpu.h"
19
20#include "SkRRect.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000021#include "SkStrokeRec.h"
22
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +000023#include "effects/GrVertexEffect.h"
24
commit-bot@chromium.org81312832013-03-22 18:34:09 +000025SK_DEFINE_INST_COUNT(GrOvalRenderer)
26
27namespace {
28
29struct CircleVertex {
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +000030 GrPoint fPos;
31 GrPoint fOffset;
commit-bot@chromium.org81312832013-03-22 18:34:09 +000032 SkScalar fOuterRadius;
33 SkScalar fInnerRadius;
34};
35
36struct EllipseVertex {
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +000037 GrPoint fPos;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000038 GrPoint fOffset;
39 GrPoint fOuterRadii;
40 GrPoint fInnerRadii;
41};
42
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000043struct DIEllipseVertex {
44 GrPoint fPos;
45 GrPoint fOuterOffset;
46 GrPoint fInnerOffset;
47};
48
commit-bot@chromium.org81312832013-03-22 18:34:09 +000049inline bool circle_stays_circle(const SkMatrix& m) {
50 return m.isSimilarity();
51}
52
53}
54
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000055///////////////////////////////////////////////////////////////////////////////
56
57/**
58 * The output of this effect is a modulation of the input color and coverage for a circle,
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +000059 * specified as offset_x, offset_y (both from center point), outer radius and inner radius.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000060 */
61
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +000062class CircleEdgeEffect : public GrVertexEffect {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000063public:
64 static GrEffectRef* Create(bool stroke) {
bsalomon@google.comd42aca32013-04-23 15:37:27 +000065 GR_CREATE_STATIC_EFFECT(gCircleStrokeEdge, CircleEdgeEffect, (true));
66 GR_CREATE_STATIC_EFFECT(gCircleFillEdge, CircleEdgeEffect, (false));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000067
68 if (stroke) {
bsalomon@google.comd42aca32013-04-23 15:37:27 +000069 gCircleStrokeEdge->ref();
70 return gCircleStrokeEdge;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000071 } else {
bsalomon@google.comd42aca32013-04-23 15:37:27 +000072 gCircleFillEdge->ref();
73 return gCircleFillEdge;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000074 }
75 }
76
skia.committer@gmail.com041e2db2013-04-03 07:01:14 +000077 virtual void getConstantColorComponents(GrColor* color,
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000078 uint32_t* validFlags) const SK_OVERRIDE {
79 *validFlags = 0;
80 }
81
82 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
83 return GrTBackendEffectFactory<CircleEdgeEffect>::getInstance();
84 }
85
86 virtual ~CircleEdgeEffect() {}
87
88 static const char* Name() { return "CircleEdge"; }
89
90 inline bool isStroked() const { return fStroke; }
91
commit-bot@chromium.org261dc562013-10-04 15:42:56 +000092 class GLEffect : public GrGLVertexEffect {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000093 public:
94 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
95 : INHERITED (factory) {}
96
commit-bot@chromium.org261dc562013-10-04 15:42:56 +000097 virtual void emitCode(GrGLFullShaderBuilder* builder,
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000098 const GrDrawEffect& drawEffect,
99 EffectKey key,
100 const char* outputColor,
101 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000102 const TransformedCoordsArray&,
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000103 const TextureSamplerArray& samplers) SK_OVERRIDE {
104 const CircleEdgeEffect& circleEffect = drawEffect.castEffect<CircleEdgeEffect>();
105 const char *vsName, *fsName;
commit-bot@chromium.org261dc562013-10-04 15:42:56 +0000106 builder->addVarying(kVec4f_GrSLType, "CircleEdge", &vsName, &fsName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000107
108 const SkString* attrName =
commit-bot@chromium.org261dc562013-10-04 15:42:56 +0000109 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
110 builder->vsCodeAppendf("\t%s = %s;\n", vsName, attrName->c_str());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000111
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000112 builder->fsCodeAppendf("\tfloat d = length(%s.xy);\n", fsName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000113 builder->fsCodeAppendf("\tfloat edgeAlpha = clamp(%s.z - d, 0.0, 1.0);\n", fsName);
114 if (circleEffect.isStroked()) {
115 builder->fsCodeAppendf("\tfloat innerAlpha = clamp(d - %s.w, 0.0, 1.0);\n", fsName);
116 builder->fsCodeAppend("\tedgeAlpha *= innerAlpha;\n");
117 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000118
commit-bot@chromium.org824c3462013-10-10 06:30:18 +0000119 builder->fsCodeAppendf("\t%s = %s;\n", outputColor,
commit-bot@chromium.orga34995e2013-10-23 05:42:03 +0000120 (GrGLSLExpr4(inputColor) * GrGLSLExpr1("edgeAlpha")).c_str());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000121 }
122
123 static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
124 const CircleEdgeEffect& circleEffect = drawEffect.castEffect<CircleEdgeEffect>();
125
126 return circleEffect.isStroked() ? 0x1 : 0x0;
127 }
128
129 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {}
130
131 private:
commit-bot@chromium.org261dc562013-10-04 15:42:56 +0000132 typedef GrGLVertexEffect INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000133 };
134
135
136private:
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +0000137 CircleEdgeEffect(bool stroke) : GrVertexEffect() {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000138 this->addVertexAttrib(kVec4f_GrSLType);
139 fStroke = stroke;
140 }
141
142 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
143 const CircleEdgeEffect& cee = CastEffect<CircleEdgeEffect>(other);
144 return cee.fStroke == fStroke;
145 }
146
147 bool fStroke;
148
149 GR_DECLARE_EFFECT_TEST;
150
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +0000151 typedef GrVertexEffect INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000152};
153
154GR_DEFINE_EFFECT_TEST(CircleEdgeEffect);
155
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000156GrEffectRef* CircleEdgeEffect::TestCreate(SkRandom* random,
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000157 GrContext* context,
158 const GrDrawTargetCaps&,
159 GrTexture* textures[]) {
160 return CircleEdgeEffect::Create(random->nextBool());
161}
162
163///////////////////////////////////////////////////////////////////////////////
164
165/**
166 * 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 +0000167 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
168 * in both x and y directions.
169 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000170 * 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 +0000171 */
172
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +0000173class EllipseEdgeEffect : public GrVertexEffect {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000174public:
175 static GrEffectRef* Create(bool stroke) {
bsalomon@google.comd42aca32013-04-23 15:37:27 +0000176 GR_CREATE_STATIC_EFFECT(gEllipseStrokeEdge, EllipseEdgeEffect, (true));
177 GR_CREATE_STATIC_EFFECT(gEllipseFillEdge, EllipseEdgeEffect, (false));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000178
179 if (stroke) {
bsalomon@google.comd42aca32013-04-23 15:37:27 +0000180 gEllipseStrokeEdge->ref();
181 return gEllipseStrokeEdge;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000182 } else {
bsalomon@google.comd42aca32013-04-23 15:37:27 +0000183 gEllipseFillEdge->ref();
184 return gEllipseFillEdge;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000185 }
186 }
187
skia.committer@gmail.com041e2db2013-04-03 07:01:14 +0000188 virtual void getConstantColorComponents(GrColor* color,
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000189 uint32_t* validFlags) const SK_OVERRIDE {
190 *validFlags = 0;
191 }
192
193 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
194 return GrTBackendEffectFactory<EllipseEdgeEffect>::getInstance();
195 }
196
197 virtual ~EllipseEdgeEffect() {}
198
199 static const char* Name() { return "EllipseEdge"; }
200
201 inline bool isStroked() const { return fStroke; }
202
commit-bot@chromium.org261dc562013-10-04 15:42:56 +0000203 class GLEffect : public GrGLVertexEffect {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000204 public:
205 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
206 : INHERITED (factory) {}
207
commit-bot@chromium.org261dc562013-10-04 15:42:56 +0000208 virtual void emitCode(GrGLFullShaderBuilder* builder,
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000209 const GrDrawEffect& drawEffect,
210 EffectKey key,
211 const char* outputColor,
212 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000213 const TransformedCoordsArray&,
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000214 const TextureSamplerArray& samplers) SK_OVERRIDE {
215 const EllipseEdgeEffect& ellipseEffect = drawEffect.castEffect<EllipseEdgeEffect>();
216
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000217 const char *vsOffsetName, *fsOffsetName;
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000218 const char *vsRadiiName, *fsRadiiName;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000219
commit-bot@chromium.org261dc562013-10-04 15:42:56 +0000220 builder->addVarying(kVec2f_GrSLType, "EllipseOffsets", &vsOffsetName, &fsOffsetName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000221 const SkString* attr0Name =
commit-bot@chromium.org261dc562013-10-04 15:42:56 +0000222 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
223 builder->vsCodeAppendf("\t%s = %s;\n", vsOffsetName, attr0Name->c_str());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000224
commit-bot@chromium.org261dc562013-10-04 15:42:56 +0000225 builder->addVarying(kVec4f_GrSLType, "EllipseRadii", &vsRadiiName, &fsRadiiName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000226 const SkString* attr1Name =
commit-bot@chromium.org261dc562013-10-04 15:42:56 +0000227 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[1]);
228 builder->vsCodeAppendf("\t%s = %s;\n", vsRadiiName, attr1Name->c_str());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000229
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000230 // for outer curve
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000231 builder->fsCodeAppendf("\tvec2 scaledOffset = %s*%s.xy;\n", fsOffsetName, fsRadiiName);
232 builder->fsCodeAppend("\tfloat test = dot(scaledOffset, scaledOffset) - 1.0;\n");
233 builder->fsCodeAppendf("\tvec2 grad = 2.0*scaledOffset*%s.xy;\n", fsRadiiName);
jvanverth@google.comd1b5b142013-07-02 14:46:03 +0000234 builder->fsCodeAppend("\tfloat grad_dot = dot(grad, grad);\n");
235 // we need to clamp the length^2 of the gradiant vector to a non-zero value, because
236 // on the Nexus 4 the undefined result of inversesqrt(0) drops out an entire tile
237 // TODO: restrict this to Adreno-only
238 builder->fsCodeAppend("\tgrad_dot = max(grad_dot, 1.0e-4);\n");
239 builder->fsCodeAppend("\tfloat invlen = inversesqrt(grad_dot);\n");
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000240 builder->fsCodeAppend("\tfloat edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);\n");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000241
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000242 // for inner curve
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000243 if (ellipseEffect.isStroked()) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000244 builder->fsCodeAppendf("\tscaledOffset = %s*%s.zw;\n", fsOffsetName, fsRadiiName);
245 builder->fsCodeAppend("\ttest = dot(scaledOffset, scaledOffset) - 1.0;\n");
246 builder->fsCodeAppendf("\tgrad = 2.0*scaledOffset*%s.zw;\n", fsRadiiName);
247 builder->fsCodeAppend("\tinvlen = inversesqrt(dot(grad, grad));\n");
248 builder->fsCodeAppend("\tedgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);\n");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000249 }
250
commit-bot@chromium.org824c3462013-10-10 06:30:18 +0000251 builder->fsCodeAppendf("\t%s = %s;\n", outputColor,
commit-bot@chromium.orga34995e2013-10-23 05:42:03 +0000252 (GrGLSLExpr4(inputColor) * GrGLSLExpr1("edgeAlpha")).c_str());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000253 }
254
255 static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
256 const EllipseEdgeEffect& ellipseEffect = drawEffect.castEffect<EllipseEdgeEffect>();
257
258 return ellipseEffect.isStroked() ? 0x1 : 0x0;
259 }
260
261 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {
262 }
263
264 private:
commit-bot@chromium.org261dc562013-10-04 15:42:56 +0000265 typedef GrGLVertexEffect INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000266 };
267
268private:
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +0000269 EllipseEdgeEffect(bool stroke) : GrVertexEffect() {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000270 this->addVertexAttrib(kVec2f_GrSLType);
271 this->addVertexAttrib(kVec4f_GrSLType);
272 fStroke = stroke;
273 }
274
275 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
276 const EllipseEdgeEffect& eee = CastEffect<EllipseEdgeEffect>(other);
277 return eee.fStroke == fStroke;
278 }
279
280 bool fStroke;
281
282 GR_DECLARE_EFFECT_TEST;
283
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +0000284 typedef GrVertexEffect INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000285};
286
287GR_DEFINE_EFFECT_TEST(EllipseEdgeEffect);
288
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000289GrEffectRef* EllipseEdgeEffect::TestCreate(SkRandom* random,
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000290 GrContext* context,
291 const GrDrawTargetCaps&,
292 GrTexture* textures[]) {
293 return EllipseEdgeEffect::Create(random->nextBool());
294}
295
296///////////////////////////////////////////////////////////////////////////////
297
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000298/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000299 * 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 +0000300 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
301 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
302 * using differentials.
303 *
304 * The result is device-independent and can be used with any affine matrix.
305 */
306
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +0000307class DIEllipseEdgeEffect : public GrVertexEffect {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000308public:
309 enum Mode { kStroke = 0, kHairline, kFill };
310
311 static GrEffectRef* Create(Mode mode) {
312 GR_CREATE_STATIC_EFFECT(gEllipseStrokeEdge, DIEllipseEdgeEffect, (kStroke));
313 GR_CREATE_STATIC_EFFECT(gEllipseHairlineEdge, DIEllipseEdgeEffect, (kHairline));
314 GR_CREATE_STATIC_EFFECT(gEllipseFillEdge, DIEllipseEdgeEffect, (kFill));
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000315
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000316 if (kStroke == mode) {
317 gEllipseStrokeEdge->ref();
318 return gEllipseStrokeEdge;
319 } else if (kHairline == mode) {
320 gEllipseHairlineEdge->ref();
321 return gEllipseHairlineEdge;
322 } else {
323 gEllipseFillEdge->ref();
324 return gEllipseFillEdge;
325 }
326 }
327
328 virtual void getConstantColorComponents(GrColor* color,
329 uint32_t* validFlags) const SK_OVERRIDE {
330 *validFlags = 0;
331 }
332
333 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
334 return GrTBackendEffectFactory<DIEllipseEdgeEffect>::getInstance();
335 }
336
337 virtual ~DIEllipseEdgeEffect() {}
338
339 static const char* Name() { return "DIEllipseEdge"; }
340
341 inline Mode getMode() const { return fMode; }
342
commit-bot@chromium.org261dc562013-10-04 15:42:56 +0000343 class GLEffect : public GrGLVertexEffect {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000344 public:
345 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
346 : INHERITED (factory) {}
347
commit-bot@chromium.org261dc562013-10-04 15:42:56 +0000348 virtual void emitCode(GrGLFullShaderBuilder* builder,
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000349 const GrDrawEffect& drawEffect,
350 EffectKey key,
351 const char* outputColor,
352 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000353 const TransformedCoordsArray&,
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000354 const TextureSamplerArray& samplers) SK_OVERRIDE {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000355 const DIEllipseEdgeEffect& ellipseEffect = drawEffect.castEffect<DIEllipseEdgeEffect>();
356
357 SkAssertResult(builder->enableFeature(
358 GrGLShaderBuilder::kStandardDerivatives_GLSLFeature));
359
commit-bot@chromium.org96a7a962013-09-06 19:46:48 +0000360 const char *vsOffsetName0, *fsOffsetName0;
commit-bot@chromium.org261dc562013-10-04 15:42:56 +0000361 builder->addVarying(kVec2f_GrSLType, "EllipseOffsets0",
commit-bot@chromium.org96a7a962013-09-06 19:46:48 +0000362 &vsOffsetName0, &fsOffsetName0);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000363 const SkString* attr0Name =
commit-bot@chromium.org261dc562013-10-04 15:42:56 +0000364 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
365 builder->vsCodeAppendf("\t%s = %s;\n", vsOffsetName0, attr0Name->c_str());
commit-bot@chromium.org96a7a962013-09-06 19:46:48 +0000366 const char *vsOffsetName1, *fsOffsetName1;
commit-bot@chromium.org261dc562013-10-04 15:42:56 +0000367 builder->addVarying(kVec2f_GrSLType, "EllipseOffsets1",
commit-bot@chromium.org96a7a962013-09-06 19:46:48 +0000368 &vsOffsetName1, &fsOffsetName1);
369 const SkString* attr1Name =
commit-bot@chromium.org261dc562013-10-04 15:42:56 +0000370 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[1]);
371 builder->vsCodeAppendf("\t%s = %s;\n", vsOffsetName1, attr1Name->c_str());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000372
373 // for outer curve
commit-bot@chromium.org96a7a962013-09-06 19:46:48 +0000374 builder->fsCodeAppendf("\tvec2 scaledOffset = %s.xy;\n", fsOffsetName0);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000375 builder->fsCodeAppend("\tfloat test = dot(scaledOffset, scaledOffset) - 1.0;\n");
commit-bot@chromium.org96a7a962013-09-06 19:46:48 +0000376 builder->fsCodeAppendf("\tvec2 duvdx = dFdx(%s);\n", fsOffsetName0);
377 builder->fsCodeAppendf("\tvec2 duvdy = dFdy(%s);\n", fsOffsetName0);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000378 builder->fsCodeAppendf("\tvec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,\n"
379 "\t 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);\n",
commit-bot@chromium.org96a7a962013-09-06 19:46:48 +0000380 fsOffsetName0, fsOffsetName0, fsOffsetName0, fsOffsetName0);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000381
382 builder->fsCodeAppend("\tfloat grad_dot = dot(grad, grad);\n");
383 // we need to clamp the length^2 of the gradiant vector to a non-zero value, because
384 // on the Nexus 4 the undefined result of inversesqrt(0) drops out an entire tile
385 // TODO: restrict this to Adreno-only
386 builder->fsCodeAppend("\tgrad_dot = max(grad_dot, 1.0e-4);\n");
387 builder->fsCodeAppend("\tfloat invlen = inversesqrt(grad_dot);\n");
388 if (kHairline == ellipseEffect.getMode()) {
389 // can probably do this with one step
390 builder->fsCodeAppend("\tfloat edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);\n");
391 builder->fsCodeAppend("\tedgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);\n");
392 } else {
393 builder->fsCodeAppend("\tfloat edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);\n");
394 }
395
396 // for inner curve
397 if (kStroke == ellipseEffect.getMode()) {
commit-bot@chromium.org96a7a962013-09-06 19:46:48 +0000398 builder->fsCodeAppendf("\tscaledOffset = %s.xy;\n", fsOffsetName1);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000399 builder->fsCodeAppend("\ttest = dot(scaledOffset, scaledOffset) - 1.0;\n");
commit-bot@chromium.org96a7a962013-09-06 19:46:48 +0000400 builder->fsCodeAppendf("\tduvdx = dFdx(%s);\n", fsOffsetName1);
401 builder->fsCodeAppendf("\tduvdy = dFdy(%s);\n", fsOffsetName1);
402 builder->fsCodeAppendf("\tgrad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,\n"
403 "\t 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);\n",
404 fsOffsetName1, fsOffsetName1, fsOffsetName1, fsOffsetName1);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000405 builder->fsCodeAppend("\tinvlen = inversesqrt(dot(grad, grad));\n");
406 builder->fsCodeAppend("\tedgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);\n");
407 }
408
commit-bot@chromium.org824c3462013-10-10 06:30:18 +0000409 builder->fsCodeAppendf("\t%s = %s;\n", outputColor,
commit-bot@chromium.orga34995e2013-10-23 05:42:03 +0000410 (GrGLSLExpr4(inputColor) * GrGLSLExpr1("edgeAlpha")).c_str());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000411 }
412
413 static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
414 const DIEllipseEdgeEffect& ellipseEffect = drawEffect.castEffect<DIEllipseEdgeEffect>();
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000415
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000416 return ellipseEffect.getMode();
417 }
418
419 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {
420 }
421
422 private:
commit-bot@chromium.org261dc562013-10-04 15:42:56 +0000423 typedef GrGLVertexEffect INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000424 };
425
426private:
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +0000427 DIEllipseEdgeEffect(Mode mode) : GrVertexEffect() {
commit-bot@chromium.org96a7a962013-09-06 19:46:48 +0000428 this->addVertexAttrib(kVec2f_GrSLType);
429 this->addVertexAttrib(kVec2f_GrSLType);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000430 fMode = mode;
431 }
432
433 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
434 const DIEllipseEdgeEffect& eee = CastEffect<DIEllipseEdgeEffect>(other);
435 return eee.fMode == fMode;
436 }
437
438 Mode fMode;
439
440 GR_DECLARE_EFFECT_TEST;
441
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +0000442 typedef GrVertexEffect INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000443};
444
445GR_DEFINE_EFFECT_TEST(DIEllipseEdgeEffect);
446
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000447GrEffectRef* DIEllipseEdgeEffect::TestCreate(SkRandom* random,
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000448 GrContext* context,
449 const GrDrawTargetCaps&,
450 GrTexture* textures[]) {
451 return DIEllipseEdgeEffect::Create((Mode)(random->nextRangeU(0,2)));
452}
453
454///////////////////////////////////////////////////////////////////////////////
455
commit-bot@chromium.orgef284a82013-07-11 22:29:29 +0000456void GrOvalRenderer::reset() {
commit-bot@chromium.orga4de8c22013-09-09 13:38:37 +0000457 SkSafeSetNull(fRRectIndexBuffer);
commit-bot@chromium.orgef284a82013-07-11 22:29:29 +0000458}
459
commit-bot@chromium.org37d883d2013-05-02 13:11:22 +0000460bool GrOvalRenderer::drawOval(GrDrawTarget* target, const GrContext* context, bool useAA,
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000461 const SkRect& oval, const SkStrokeRec& stroke)
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000462{
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000463 bool useCoverageAA = useAA &&
464 !target->getDrawState().getRenderTarget()->isMultisampled() &&
465 !target->shouldDisableCoverageAAForBlend();
466
467 if (!useCoverageAA) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000468 return false;
469 }
470
471 const SkMatrix& vm = context->getMatrix();
472
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000473 // we can draw circles
474 if (SkScalarNearlyEqual(oval.width(), oval.height())
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000475 && circle_stays_circle(vm)) {
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000476 this->drawCircle(target, useCoverageAA, oval, stroke);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000477 // if we have shader derivative support, render as device-independent
478 } else if (target->caps()->shaderDerivativeSupport()) {
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000479 return this->drawDIEllipse(target, useCoverageAA, oval, stroke);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000480 // otherwise axis-aligned ellipses only
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000481 } else if (vm.rectStaysRect()) {
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000482 return this->drawEllipse(target, useCoverageAA, oval, stroke);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000483 } else {
484 return false;
485 }
486
487 return true;
488}
489
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000490///////////////////////////////////////////////////////////////////////////////
491
robertphillips@google.com42903302013-04-20 12:26:07 +0000492// position + edge
493extern const GrVertexAttrib gCircleVertexAttribs[] = {
494 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
495 {kVec4f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding}
496};
497
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000498void GrOvalRenderer::drawCircle(GrDrawTarget* target,
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000499 bool useCoverageAA,
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000500 const SkRect& circle,
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000501 const SkStrokeRec& stroke)
502{
503 GrDrawState* drawState = target->drawState();
504
505 const SkMatrix& vm = drawState->getViewMatrix();
506 GrPoint center = GrPoint::Make(circle.centerX(), circle.centerY());
507 vm.mapPoints(&center, 1);
508 SkScalar radius = vm.mapRadius(SkScalarHalf(circle.width()));
509 SkScalar strokeWidth = vm.mapRadius(stroke.getWidth());
510
bsalomon@google.com137f1342013-05-29 21:27:53 +0000511 GrDrawState::AutoViewMatrixRestore avmr;
512 if (!avmr.setIdentity(drawState)) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000513 return;
514 }
515
robertphillips@google.com42903302013-04-20 12:26:07 +0000516 drawState->setVertexAttribs<gCircleVertexAttribs>(SK_ARRAY_COUNT(gCircleVertexAttribs));
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000517 SkASSERT(sizeof(CircleVertex) == drawState->getVertexSize());
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000518
519 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
520 if (!geo.succeeded()) {
521 GrPrintf("Failed to get space for vertices!\n");
522 return;
523 }
524
525 CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices());
526
527 SkStrokeRec::Style style = stroke.getStyle();
528 bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style);
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000529
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000530 SkScalar innerRadius = 0.0f;
531 SkScalar outerRadius = radius;
532 SkScalar halfWidth = 0;
533 if (style != SkStrokeRec::kFill_Style) {
534 if (SkScalarNearlyZero(strokeWidth)) {
535 halfWidth = SK_ScalarHalf;
536 } else {
537 halfWidth = SkScalarHalf(strokeWidth);
538 }
539
540 outerRadius += halfWidth;
541 if (isStroked) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000542 innerRadius = radius - halfWidth;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000543 }
544 }
545
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +0000546 GrEffectRef* effect = CircleEdgeEffect::Create(isStroked && innerRadius > 0);
547 static const int kCircleEdgeAttrIndex = 1;
548 drawState->addCoverageEffect(effect, kCircleEdgeAttrIndex)->unref();
549
bsalomon@google.com58e30fe2013-04-01 19:01:20 +0000550 // The radii are outset for two reasons. First, it allows the shader to simply perform
551 // clamp(distance-to-center - radius, 0, 1). Second, the outer radius is used to compute the
552 // verts of the bounding box that is rendered and the outset ensures the box will cover all
553 // pixels partially covered by the circle.
554 outerRadius += SK_ScalarHalf;
555 innerRadius -= SK_ScalarHalf;
556
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000557 SkRect bounds = SkRect::MakeLTRB(
bsalomon@google.com58e30fe2013-04-01 19:01:20 +0000558 center.fX - outerRadius,
559 center.fY - outerRadius,
560 center.fX + outerRadius,
561 center.fY + outerRadius
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000562 );
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000563
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000564 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000565 verts[0].fOffset = SkPoint::Make(-outerRadius, -outerRadius);
566 verts[0].fOuterRadius = outerRadius;
567 verts[0].fInnerRadius = innerRadius;
568
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000569 verts[1].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
skia.committer@gmail.com46746762013-04-12 07:01:51 +0000570 verts[1].fOffset = SkPoint::Make(outerRadius, -outerRadius);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000571 verts[1].fOuterRadius = outerRadius;
572 verts[1].fInnerRadius = innerRadius;
573
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000574 verts[2].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000575 verts[2].fOffset = SkPoint::Make(-outerRadius, outerRadius);
576 verts[2].fOuterRadius = outerRadius;
577 verts[2].fInnerRadius = innerRadius;
578
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000579 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000580 verts[3].fOffset = SkPoint::Make(outerRadius, outerRadius);
581 verts[3].fOuterRadius = outerRadius;
582 verts[3].fInnerRadius = innerRadius;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000583
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000584 target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4, &bounds);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000585}
586
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000587///////////////////////////////////////////////////////////////////////////////
588
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000589// position + offset + 1/radii
robertphillips@google.com42903302013-04-20 12:26:07 +0000590extern const GrVertexAttrib gEllipseVertexAttribs[] = {
591 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
592 {kVec2f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding},
593 {kVec4f_GrVertexAttribType, 2*sizeof(GrPoint), kEffect_GrVertexAttribBinding}
594};
595
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000596// position + offsets
597extern const GrVertexAttrib gDIEllipseVertexAttribs[] = {
598 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
commit-bot@chromium.org96a7a962013-09-06 19:46:48 +0000599 {kVec2f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding},
600 {kVec2f_GrVertexAttribType, 2*sizeof(GrPoint), kEffect_GrVertexAttribBinding},
robertphillips@google.com42903302013-04-20 12:26:07 +0000601};
602
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +0000603bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000604 bool useCoverageAA,
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000605 const SkRect& ellipse,
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000606 const SkStrokeRec& stroke)
607{
608 GrDrawState* drawState = target->drawState();
609#ifdef SK_DEBUG
610 {
611 // we should have checked for this previously
612 bool isAxisAlignedEllipse = drawState->getViewMatrix().rectStaysRect();
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000613 SkASSERT(useCoverageAA && isAxisAlignedEllipse);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000614 }
615#endif
616
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000617 // do any matrix crunching before we reset the draw state for device coords
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000618 const SkMatrix& vm = drawState->getViewMatrix();
619 GrPoint center = GrPoint::Make(ellipse.centerX(), ellipse.centerY());
620 vm.mapPoints(&center, 1);
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000621 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
622 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
skia.committer@gmail.com64b682c2013-04-20 07:01:07 +0000623 SkScalar xRadius = SkScalarAbs(vm[SkMatrix::kMScaleX]*ellipseXRadius +
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000624 vm[SkMatrix::kMSkewY]*ellipseYRadius);
skia.committer@gmail.com64b682c2013-04-20 07:01:07 +0000625 SkScalar yRadius = SkScalarAbs(vm[SkMatrix::kMSkewX]*ellipseXRadius +
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000626 vm[SkMatrix::kMScaleY]*ellipseYRadius);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000627
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000628 // do (potentially) anisotropic mapping of stroke
629 SkVector scaledStroke;
630 SkScalar strokeWidth = stroke.getWidth();
631 scaledStroke.fX = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMScaleX] + vm[SkMatrix::kMSkewY]));
632 scaledStroke.fY = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMSkewX] + vm[SkMatrix::kMScaleY]));
633
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000634 SkStrokeRec::Style style = stroke.getStyle();
635 bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style);
636
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000637 SkScalar innerXRadius = 0;
638 SkScalar innerYRadius = 0;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000639 if (SkStrokeRec::kFill_Style != style) {
640 if (SkScalarNearlyZero(scaledStroke.length())) {
641 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
642 } else {
643 scaledStroke.scale(SK_ScalarHalf);
644 }
645
646 // we only handle thick strokes for near-circular ellipses
647 if (scaledStroke.length() > SK_ScalarHalf &&
648 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
649 return false;
650 }
651
652 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
653 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
654 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
655 return false;
656 }
657
658 // this is legit only if scale & translation (which should be the case at the moment)
659 if (isStroked) {
660 innerXRadius = xRadius - scaledStroke.fX;
661 innerYRadius = yRadius - scaledStroke.fY;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000662 }
663
664 xRadius += scaledStroke.fX;
665 yRadius += scaledStroke.fY;
666 }
667
bsalomon@google.com137f1342013-05-29 21:27:53 +0000668 GrDrawState::AutoViewMatrixRestore avmr;
669 if (!avmr.setIdentity(drawState)) {
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +0000670 return false;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000671 }
672
robertphillips@google.com42903302013-04-20 12:26:07 +0000673 drawState->setVertexAttribs<gEllipseVertexAttribs>(SK_ARRAY_COUNT(gEllipseVertexAttribs));
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000674 SkASSERT(sizeof(EllipseVertex) == drawState->getVertexSize());
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000675
676 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
677 if (!geo.succeeded()) {
678 GrPrintf("Failed to get space for vertices!\n");
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +0000679 return false;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000680 }
681
682 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices());
683
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +0000684 GrEffectRef* effect = EllipseEdgeEffect::Create(isStroked &&
685 innerXRadius > 0 && innerYRadius > 0);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000686
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000687 static const int kEllipseCenterAttrIndex = 1;
jvanverth@google.com6cc8d442013-09-10 18:24:37 +0000688 static const int kEllipseEdgeAttrIndex = 2;
689 drawState->addCoverageEffect(effect, kEllipseCenterAttrIndex, kEllipseEdgeAttrIndex)->unref();
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000690
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000691 // Compute the reciprocals of the radii here to save time in the shader
692 SkScalar xRadRecip = SkScalarInvert(xRadius);
693 SkScalar yRadRecip = SkScalarInvert(yRadius);
694 SkScalar xInnerRadRecip = SkScalarInvert(innerXRadius);
695 SkScalar yInnerRadRecip = SkScalarInvert(innerYRadius);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000696
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000697 // We've extended the outer x radius out half a pixel to antialias.
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000698 // This will also expand the rect so all the pixels will be captured.
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000699 // TODO: Consider if we should use sqrt(2)/2 instead
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000700 xRadius += SK_ScalarHalf;
701 yRadius += SK_ScalarHalf;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000702
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000703 SkRect bounds = SkRect::MakeLTRB(
704 center.fX - xRadius,
705 center.fY - yRadius,
706 center.fX + xRadius,
707 center.fY + yRadius
708 );
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000709
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000710 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000711 verts[0].fOffset = SkPoint::Make(-xRadius, -yRadius);
712 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
713 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000714
715 verts[1].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000716 verts[1].fOffset = SkPoint::Make(xRadius, -yRadius);
717 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
718 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000719
720 verts[2].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000721 verts[2].fOffset = SkPoint::Make(-xRadius, yRadius);
722 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
723 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000724
725 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000726 verts[3].fOffset = SkPoint::Make(xRadius, yRadius);
727 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
728 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
skia.committer@gmail.com46746762013-04-12 07:01:51 +0000729
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000730 target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4, &bounds);
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +0000731
732 return true;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000733}
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000734
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000735bool GrOvalRenderer::drawDIEllipse(GrDrawTarget* target,
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000736 bool useCoverageAA,
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000737 const SkRect& ellipse,
738 const SkStrokeRec& stroke)
739{
740 GrDrawState* drawState = target->drawState();
741 const SkMatrix& vm = drawState->getViewMatrix();
742
743 GrPoint center = GrPoint::Make(ellipse.centerX(), ellipse.centerY());
744 SkScalar xRadius = SkScalarHalf(ellipse.width());
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000745 SkScalar yRadius = SkScalarHalf(ellipse.height());
746
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000747 SkStrokeRec::Style style = stroke.getStyle();
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000748 DIEllipseEdgeEffect::Mode mode = (SkStrokeRec::kStroke_Style == style) ?
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000749 DIEllipseEdgeEffect::kStroke :
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000750 (SkStrokeRec::kHairline_Style == style) ?
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000751 DIEllipseEdgeEffect::kHairline : DIEllipseEdgeEffect::kFill;
752
753 SkScalar innerXRadius = 0;
754 SkScalar innerYRadius = 0;
755 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
756 SkScalar strokeWidth = stroke.getWidth();
757
758 if (SkScalarNearlyZero(strokeWidth)) {
759 strokeWidth = SK_ScalarHalf;
760 } else {
761 strokeWidth *= SK_ScalarHalf;
762 }
763
764 // we only handle thick strokes for near-circular ellipses
765 if (strokeWidth > SK_ScalarHalf &&
766 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
767 return false;
768 }
769
770 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
771 if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius ||
772 strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) {
773 return false;
774 }
775
776 // set inner radius (if needed)
777 if (SkStrokeRec::kStroke_Style == style) {
778 innerXRadius = xRadius - strokeWidth;
779 innerYRadius = yRadius - strokeWidth;
780 }
781
782 xRadius += strokeWidth;
783 yRadius += strokeWidth;
784 }
785 if (DIEllipseEdgeEffect::kStroke == mode) {
786 mode = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseEdgeEffect::kStroke :
787 DIEllipseEdgeEffect::kFill;
788 }
789 SkScalar innerRatioX = SkScalarDiv(xRadius, innerXRadius);
790 SkScalar innerRatioY = SkScalarDiv(yRadius, innerYRadius);
791
792 drawState->setVertexAttribs<gDIEllipseVertexAttribs>(SK_ARRAY_COUNT(gDIEllipseVertexAttribs));
793 SkASSERT(sizeof(DIEllipseVertex) == drawState->getVertexSize());
794
795 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
796 if (!geo.succeeded()) {
797 GrPrintf("Failed to get space for vertices!\n");
798 return false;
799 }
800
801 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(geo.vertices());
802
803 GrEffectRef* effect = DIEllipseEdgeEffect::Create(mode);
804
805 static const int kEllipseOuterOffsetAttrIndex = 1;
806 static const int kEllipseInnerOffsetAttrIndex = 2;
807 drawState->addCoverageEffect(effect, kEllipseOuterOffsetAttrIndex,
808 kEllipseInnerOffsetAttrIndex)->unref();
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000809
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000810 // This expands the outer rect so that after CTM we end up with a half-pixel border
811 SkScalar a = vm[SkMatrix::kMScaleX];
812 SkScalar b = vm[SkMatrix::kMSkewX];
813 SkScalar c = vm[SkMatrix::kMSkewY];
814 SkScalar d = vm[SkMatrix::kMScaleY];
815 SkScalar geoDx = SkScalarDiv(SK_ScalarHalf, SkScalarSqrt(a*a + c*c));
816 SkScalar geoDy = SkScalarDiv(SK_ScalarHalf, SkScalarSqrt(b*b + d*d));
817 // This adjusts the "radius" to include the half-pixel border
818 SkScalar offsetDx = SkScalarDiv(geoDx, xRadius);
819 SkScalar offsetDy = SkScalarDiv(geoDy, yRadius);
820
821 SkRect bounds = SkRect::MakeLTRB(
822 center.fX - xRadius - geoDx,
823 center.fY - yRadius - geoDy,
824 center.fX + xRadius + geoDx,
825 center.fY + yRadius + geoDy
826 );
827
828 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
829 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
830 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
831
832 verts[1].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
833 verts[1].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
834 verts[1].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
835
836 verts[2].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
837 verts[2].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
838 verts[2].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
839
840 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
841 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
842 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
843
844 target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4, &bounds);
845
846 return true;
847}
848
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000849///////////////////////////////////////////////////////////////////////////////
850
851static const uint16_t gRRectIndices[] = {
852 // corners
853 0, 1, 5, 0, 5, 4,
854 2, 3, 7, 2, 7, 6,
855 8, 9, 13, 8, 13, 12,
856 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000857
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000858 // edges
859 1, 2, 6, 1, 6, 5,
860 4, 5, 9, 4, 9, 8,
861 6, 7, 11, 6, 11, 10,
862 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000863
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000864 // center
865 // we place this at the end so that we can ignore these indices when rendering stroke-only
866 5, 6, 10, 5, 10, 9
867};
868
869
870GrIndexBuffer* GrOvalRenderer::rRectIndexBuffer(GrGpu* gpu) {
871 if (NULL == fRRectIndexBuffer) {
872 fRRectIndexBuffer =
873 gpu->createIndexBuffer(sizeof(gRRectIndices), false);
874 if (NULL != fRRectIndexBuffer) {
commit-bot@chromium.org515dcd32013-08-28 14:17:03 +0000875#ifdef SK_DEBUG
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000876 bool updated =
877#endif
878 fRRectIndexBuffer->updateData(gRRectIndices,
879 sizeof(gRRectIndices));
880 GR_DEBUGASSERT(updated);
881 }
882 }
883 return fRRectIndexBuffer;
884}
885
skia.committer@gmail.com2fd42c42013-05-03 07:01:00 +0000886bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, bool useAA,
commit-bot@chromium.org37d883d2013-05-02 13:11:22 +0000887 const SkRRect& rrect, const SkStrokeRec& stroke)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000888{
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000889 bool useCoverageAA = useAA &&
890 !target->getDrawState().getRenderTarget()->isMultisampled() &&
891 !target->shouldDisableCoverageAAForBlend();
892
commit-bot@chromium.org37d883d2013-05-02 13:11:22 +0000893 // only anti-aliased rrects for now
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000894 if (!useCoverageAA) {
commit-bot@chromium.org37d883d2013-05-02 13:11:22 +0000895 return false;
896 }
897
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000898 const SkMatrix& vm = context->getMatrix();
899#ifdef SK_DEBUG
900 {
901 // we should have checked for this previously
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000902 SkASSERT(useCoverageAA && vm.rectStaysRect() && rrect.isSimple());
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000903 }
904#endif
905
906 // do any matrix crunching before we reset the draw state for device coords
907 const SkRect& rrectBounds = rrect.getBounds();
908 SkRect bounds;
909 vm.mapRect(&bounds, rrectBounds);
910
911 SkVector radii = rrect.getSimpleRadii();
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000912 SkScalar xRadius = SkScalarAbs(vm[SkMatrix::kMScaleX]*radii.fX +
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000913 vm[SkMatrix::kMSkewY]*radii.fY);
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000914 SkScalar yRadius = SkScalarAbs(vm[SkMatrix::kMSkewX]*radii.fX +
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000915 vm[SkMatrix::kMScaleY]*radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000916
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000917 // if hairline stroke is greater than radius, we don't handle that right now
918 SkStrokeRec::Style style = stroke.getStyle();
919 if (SkStrokeRec::kHairline_Style == style &&
commit-bot@chromium.orgeacc4892013-11-07 16:34:53 +0000920 (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000921 return false;
922 }
923
924 // do (potentially) anisotropic mapping of stroke
925 SkVector scaledStroke;
926 SkScalar strokeWidth = stroke.getWidth();
927 scaledStroke.fX = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMScaleX] + vm[SkMatrix::kMSkewY]));
928 scaledStroke.fY = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMSkewX] + vm[SkMatrix::kMScaleY]));
929
930 // if half of strokewidth is greater than radius, we don't handle that right now
commit-bot@chromium.orgeacc4892013-11-07 16:34:53 +0000931 if (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000932 return false;
933 }
934
935 // reset to device coordinates
936 GrDrawState* drawState = target->drawState();
bsalomon@google.com137f1342013-05-29 21:27:53 +0000937 GrDrawState::AutoViewMatrixRestore avmr;
938 if (!avmr.setIdentity(drawState)) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000939 return false;
940 }
941
942 bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style);
943
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000944 GrIndexBuffer* indexBuffer = this->rRectIndexBuffer(context->getGpu());
945 if (NULL == indexBuffer) {
946 GrPrintf("Failed to create index buffer!\n");
947 return false;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000948 }
949
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000950 // if the corners are circles, use the circle renderer
951 if ((!isStroked || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) {
952 drawState->setVertexAttribs<gCircleVertexAttribs>(SK_ARRAY_COUNT(gCircleVertexAttribs));
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000953 SkASSERT(sizeof(CircleVertex) == drawState->getVertexSize());
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000954
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000955 GrDrawTarget::AutoReleaseGeometry geo(target, 16, 0);
956 if (!geo.succeeded()) {
957 GrPrintf("Failed to get space for vertices!\n");
958 return false;
959 }
960 CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices());
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000961
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000962 SkScalar innerRadius = 0.0f;
963 SkScalar outerRadius = xRadius;
964 SkScalar halfWidth = 0;
965 if (style != SkStrokeRec::kFill_Style) {
966 if (SkScalarNearlyZero(scaledStroke.fX)) {
967 halfWidth = SK_ScalarHalf;
968 } else {
969 halfWidth = SkScalarHalf(scaledStroke.fX);
970 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000971
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000972 if (isStroked) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000973 innerRadius = xRadius - halfWidth;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000974 }
975 outerRadius += halfWidth;
976 bounds.outset(halfWidth, halfWidth);
977 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000978
commit-bot@chromium.orgeacc4892013-11-07 16:34:53 +0000979 isStroked = (isStroked && innerRadius >= 0);
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +0000980
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000981 GrEffectRef* effect = CircleEdgeEffect::Create(isStroked);
982 static const int kCircleEdgeAttrIndex = 1;
bsalomon@google.comeb6879f2013-06-13 19:34:18 +0000983 drawState->addCoverageEffect(effect, kCircleEdgeAttrIndex)->unref();
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000984
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000985 // The radii are outset for two reasons. First, it allows the shader to simply perform
986 // clamp(distance-to-center - radius, 0, 1). Second, the outer radius is used to compute the
987 // verts of the bounding box that is rendered and the outset ensures the box will cover all
988 // pixels partially covered by the circle.
989 outerRadius += SK_ScalarHalf;
990 innerRadius -= SK_ScalarHalf;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000991
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000992 // Expand the rect so all the pixels will be captured.
993 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000994
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000995 SkScalar yCoords[4] = {
996 bounds.fTop,
997 bounds.fTop + outerRadius,
998 bounds.fBottom - outerRadius,
999 bounds.fBottom
1000 };
1001 SkScalar yOuterRadii[4] = {
1002 -outerRadius,
1003 0,
1004 0,
1005 outerRadius
1006 };
1007 for (int i = 0; i < 4; ++i) {
1008 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1009 verts->fOffset = SkPoint::Make(-outerRadius, yOuterRadii[i]);
1010 verts->fOuterRadius = outerRadius;
1011 verts->fInnerRadius = innerRadius;
1012 verts++;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001013
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001014 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
1015 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1016 verts->fOuterRadius = outerRadius;
1017 verts->fInnerRadius = innerRadius;
1018 verts++;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001019
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001020 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
1021 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1022 verts->fOuterRadius = outerRadius;
1023 verts->fInnerRadius = innerRadius;
1024 verts++;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001025
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001026 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1027 verts->fOffset = SkPoint::Make(outerRadius, yOuterRadii[i]);
1028 verts->fOuterRadius = outerRadius;
1029 verts->fInnerRadius = innerRadius;
1030 verts++;
1031 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001032
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001033 // drop out the middle quad if we're stroked
1034 int indexCnt = isStroked ? GR_ARRAY_COUNT(gRRectIndices)-6 : GR_ARRAY_COUNT(gRRectIndices);
1035 target->setIndexSourceToBuffer(indexBuffer);
1036 target->drawIndexed(kTriangles_GrPrimitiveType, 0, 0, 16, indexCnt, &bounds);
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001037
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001038 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001039 } else {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001040 drawState->setVertexAttribs<gEllipseVertexAttribs>(SK_ARRAY_COUNT(gEllipseVertexAttribs));
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +00001041 SkASSERT(sizeof(EllipseVertex) == drawState->getVertexSize());
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001042
1043 SkScalar innerXRadius = 0.0f;
1044 SkScalar innerYRadius = 0.0f;
1045 if (SkStrokeRec::kFill_Style != style) {
1046 if (SkScalarNearlyZero(scaledStroke.length())) {
1047 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1048 } else {
1049 scaledStroke.scale(SK_ScalarHalf);
1050 }
1051
1052 // we only handle thick strokes for near-circular ellipses
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +00001053 if (scaledStroke.length() > SK_ScalarHalf &&
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001054 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
1055 return false;
1056 }
1057
1058 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1059 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
1060 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
1061 return false;
1062 }
1063
1064 // this is legit only if scale & translation (which should be the case at the moment)
1065 if (isStroked) {
1066 innerXRadius = xRadius - scaledStroke.fX;
1067 innerYRadius = yRadius - scaledStroke.fY;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001068 }
1069
1070 xRadius += scaledStroke.fX;
1071 yRadius += scaledStroke.fY;
1072 bounds.outset(scaledStroke.fX, scaledStroke.fY);
1073 }
jvanverth@google.come3647412013-05-08 15:31:43 +00001074
commit-bot@chromium.orgeacc4892013-11-07 16:34:53 +00001075 isStroked = (isStroked && innerXRadius >= 0 && innerYRadius >= 0);
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +00001076
jvanverth@google.come3647412013-05-08 15:31:43 +00001077 GrDrawTarget::AutoReleaseGeometry geo(target, 16, 0);
1078 if (!geo.succeeded()) {
1079 GrPrintf("Failed to get space for vertices!\n");
1080 return false;
1081 }
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001082 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices());
jvanverth@google.come3647412013-05-08 15:31:43 +00001083
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001084 GrEffectRef* effect = EllipseEdgeEffect::Create(isStroked);
jvanverth@google.come3647412013-05-08 15:31:43 +00001085 static const int kEllipseOffsetAttrIndex = 1;
1086 static const int kEllipseRadiiAttrIndex = 2;
bsalomon@google.comeb6879f2013-06-13 19:34:18 +00001087 drawState->addCoverageEffect(effect,
1088 kEllipseOffsetAttrIndex, kEllipseRadiiAttrIndex)->unref();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001089
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001090 // Compute the reciprocals of the radii here to save time in the shader
1091 SkScalar xRadRecip = SkScalarInvert(xRadius);
1092 SkScalar yRadRecip = SkScalarInvert(yRadius);
1093 SkScalar xInnerRadRecip = SkScalarInvert(innerXRadius);
1094 SkScalar yInnerRadRecip = SkScalarInvert(innerYRadius);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001095
1096 // Extend the radii out half a pixel to antialias.
1097 SkScalar xOuterRadius = xRadius + SK_ScalarHalf;
1098 SkScalar yOuterRadius = yRadius + SK_ScalarHalf;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001099
1100 // Expand the rect so all the pixels will be captured.
1101 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1102
1103 SkScalar yCoords[4] = {
1104 bounds.fTop,
1105 bounds.fTop + yOuterRadius,
1106 bounds.fBottom - yOuterRadius,
1107 bounds.fBottom
1108 };
1109 SkScalar yOuterOffsets[4] = {
jvanverth@google.comd1b5b142013-07-02 14:46:03 +00001110 yOuterRadius,
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001111 SK_ScalarNearlyZero, // we're using inversesqrt() in the shader, so can't be exactly 0
1112 SK_ScalarNearlyZero,
1113 yOuterRadius
1114 };
1115
1116 for (int i = 0; i < 4; ++i) {
1117 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
jvanverth@google.comd1b5b142013-07-02 14:46:03 +00001118 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001119 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1120 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001121 verts++;
1122
1123 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
1124 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001125 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1126 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001127 verts++;
1128
1129 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
1130 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001131 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1132 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001133 verts++;
1134
1135 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1136 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001137 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1138 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001139 verts++;
1140 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001141
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001142 // drop out the middle quad if we're stroked
1143 int indexCnt = isStroked ? GR_ARRAY_COUNT(gRRectIndices)-6 : GR_ARRAY_COUNT(gRRectIndices);
1144 target->setIndexSourceToBuffer(indexBuffer);
1145 target->drawIndexed(kTriangles_GrPrimitiveType, 0, 0, 16, indexCnt, &bounds);
1146 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001147
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001148 return true;
1149}