|
1 /* |
|
2 * Copyright 2013 Google Inc. |
|
3 * |
|
4 * Use of this source code is governed by a BSD-style license that can be |
|
5 * found in the LICENSE file. |
|
6 */ |
|
7 |
|
8 #include "GrOvalRenderer.h" |
|
9 |
|
10 #include "GrEffect.h" |
|
11 #include "gl/GrGLEffect.h" |
|
12 #include "gl/GrGLSL.h" |
|
13 #include "gl/GrGLVertexEffect.h" |
|
14 #include "GrTBackendEffectFactory.h" |
|
15 |
|
16 #include "GrDrawState.h" |
|
17 #include "GrDrawTarget.h" |
|
18 #include "GrGpu.h" |
|
19 |
|
20 #include "SkRRect.h" |
|
21 #include "SkStrokeRec.h" |
|
22 |
|
23 #include "effects/GrVertexEffect.h" |
|
24 |
|
25 namespace { |
|
26 |
|
27 struct CircleVertex { |
|
28 GrPoint fPos; |
|
29 GrPoint fOffset; |
|
30 SkScalar fOuterRadius; |
|
31 SkScalar fInnerRadius; |
|
32 }; |
|
33 |
|
34 struct EllipseVertex { |
|
35 GrPoint fPos; |
|
36 GrPoint fOffset; |
|
37 GrPoint fOuterRadii; |
|
38 GrPoint fInnerRadii; |
|
39 }; |
|
40 |
|
41 struct DIEllipseVertex { |
|
42 GrPoint fPos; |
|
43 GrPoint fOuterOffset; |
|
44 GrPoint fInnerOffset; |
|
45 }; |
|
46 |
|
47 inline bool circle_stays_circle(const SkMatrix& m) { |
|
48 return m.isSimilarity(); |
|
49 } |
|
50 |
|
51 } |
|
52 |
|
53 /////////////////////////////////////////////////////////////////////////////// |
|
54 |
|
55 /** |
|
56 * The output of this effect is a modulation of the input color and coverage for a circle, |
|
57 * specified as offset_x, offset_y (both from center point), outer radius and inner radius. |
|
58 */ |
|
59 |
|
60 class CircleEdgeEffect : public GrVertexEffect { |
|
61 public: |
|
62 static GrEffectRef* Create(bool stroke) { |
|
63 GR_CREATE_STATIC_EFFECT(gCircleStrokeEdge, CircleEdgeEffect, (true)); |
|
64 GR_CREATE_STATIC_EFFECT(gCircleFillEdge, CircleEdgeEffect, (false)); |
|
65 |
|
66 if (stroke) { |
|
67 gCircleStrokeEdge->ref(); |
|
68 return gCircleStrokeEdge; |
|
69 } else { |
|
70 gCircleFillEdge->ref(); |
|
71 return gCircleFillEdge; |
|
72 } |
|
73 } |
|
74 |
|
75 virtual void getConstantColorComponents(GrColor* color, |
|
76 uint32_t* validFlags) const SK_OVERRIDE { |
|
77 *validFlags = 0; |
|
78 } |
|
79 |
|
80 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE { |
|
81 return GrTBackendEffectFactory<CircleEdgeEffect>::getInstance(); |
|
82 } |
|
83 |
|
84 virtual ~CircleEdgeEffect() {} |
|
85 |
|
86 static const char* Name() { return "CircleEdge"; } |
|
87 |
|
88 inline bool isStroked() const { return fStroke; } |
|
89 |
|
90 class GLEffect : public GrGLVertexEffect { |
|
91 public: |
|
92 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&) |
|
93 : INHERITED (factory) {} |
|
94 |
|
95 virtual void emitCode(GrGLFullShaderBuilder* builder, |
|
96 const GrDrawEffect& drawEffect, |
|
97 EffectKey key, |
|
98 const char* outputColor, |
|
99 const char* inputColor, |
|
100 const TransformedCoordsArray&, |
|
101 const TextureSamplerArray& samplers) SK_OVERRIDE { |
|
102 const CircleEdgeEffect& circleEffect = drawEffect.castEffect<CircleEdgeEffect>(); |
|
103 const char *vsName, *fsName; |
|
104 builder->addVarying(kVec4f_GrSLType, "CircleEdge", &vsName, &fsName); |
|
105 |
|
106 const SkString* attrName = |
|
107 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]); |
|
108 builder->vsCodeAppendf("\t%s = %s;\n", vsName, attrName->c_str()); |
|
109 |
|
110 builder->fsCodeAppendf("\tfloat d = length(%s.xy);\n", fsName); |
|
111 builder->fsCodeAppendf("\tfloat edgeAlpha = clamp(%s.z - d, 0.0, 1.0);\n", fsName); |
|
112 if (circleEffect.isStroked()) { |
|
113 builder->fsCodeAppendf("\tfloat innerAlpha = clamp(d - %s.w, 0.0, 1.0);\n", fsName); |
|
114 builder->fsCodeAppend("\tedgeAlpha *= innerAlpha;\n"); |
|
115 } |
|
116 |
|
117 builder->fsCodeAppendf("\t%s = %s;\n", outputColor, |
|
118 (GrGLSLExpr4(inputColor) * GrGLSLExpr1("edgeAlpha")).c_str()); |
|
119 } |
|
120 |
|
121 static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) { |
|
122 const CircleEdgeEffect& circleEffect = drawEffect.castEffect<CircleEdgeEffect>(); |
|
123 |
|
124 return circleEffect.isStroked() ? 0x1 : 0x0; |
|
125 } |
|
126 |
|
127 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {} |
|
128 |
|
129 private: |
|
130 typedef GrGLVertexEffect INHERITED; |
|
131 }; |
|
132 |
|
133 |
|
134 private: |
|
135 CircleEdgeEffect(bool stroke) : GrVertexEffect() { |
|
136 this->addVertexAttrib(kVec4f_GrSLType); |
|
137 fStroke = stroke; |
|
138 } |
|
139 |
|
140 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE { |
|
141 const CircleEdgeEffect& cee = CastEffect<CircleEdgeEffect>(other); |
|
142 return cee.fStroke == fStroke; |
|
143 } |
|
144 |
|
145 bool fStroke; |
|
146 |
|
147 GR_DECLARE_EFFECT_TEST; |
|
148 |
|
149 typedef GrVertexEffect INHERITED; |
|
150 }; |
|
151 |
|
152 GR_DEFINE_EFFECT_TEST(CircleEdgeEffect); |
|
153 |
|
154 GrEffectRef* CircleEdgeEffect::TestCreate(SkRandom* random, |
|
155 GrContext* context, |
|
156 const GrDrawTargetCaps&, |
|
157 GrTexture* textures[]) { |
|
158 return CircleEdgeEffect::Create(random->nextBool()); |
|
159 } |
|
160 |
|
161 /////////////////////////////////////////////////////////////////////////////// |
|
162 |
|
163 /** |
|
164 * The output of this effect is a modulation of the input color and coverage for an axis-aligned |
|
165 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii, |
|
166 * in both x and y directions. |
|
167 * |
|
168 * We are using an implicit function of x^2/a^2 + y^2/b^2 - 1 = 0. |
|
169 */ |
|
170 |
|
171 class EllipseEdgeEffect : public GrVertexEffect { |
|
172 public: |
|
173 static GrEffectRef* Create(bool stroke) { |
|
174 GR_CREATE_STATIC_EFFECT(gEllipseStrokeEdge, EllipseEdgeEffect, (true)); |
|
175 GR_CREATE_STATIC_EFFECT(gEllipseFillEdge, EllipseEdgeEffect, (false)); |
|
176 |
|
177 if (stroke) { |
|
178 gEllipseStrokeEdge->ref(); |
|
179 return gEllipseStrokeEdge; |
|
180 } else { |
|
181 gEllipseFillEdge->ref(); |
|
182 return gEllipseFillEdge; |
|
183 } |
|
184 } |
|
185 |
|
186 virtual void getConstantColorComponents(GrColor* color, |
|
187 uint32_t* validFlags) const SK_OVERRIDE { |
|
188 *validFlags = 0; |
|
189 } |
|
190 |
|
191 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE { |
|
192 return GrTBackendEffectFactory<EllipseEdgeEffect>::getInstance(); |
|
193 } |
|
194 |
|
195 virtual ~EllipseEdgeEffect() {} |
|
196 |
|
197 static const char* Name() { return "EllipseEdge"; } |
|
198 |
|
199 inline bool isStroked() const { return fStroke; } |
|
200 |
|
201 class GLEffect : public GrGLVertexEffect { |
|
202 public: |
|
203 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&) |
|
204 : INHERITED (factory) {} |
|
205 |
|
206 virtual void emitCode(GrGLFullShaderBuilder* builder, |
|
207 const GrDrawEffect& drawEffect, |
|
208 EffectKey key, |
|
209 const char* outputColor, |
|
210 const char* inputColor, |
|
211 const TransformedCoordsArray&, |
|
212 const TextureSamplerArray& samplers) SK_OVERRIDE { |
|
213 const EllipseEdgeEffect& ellipseEffect = drawEffect.castEffect<EllipseEdgeEffect>(); |
|
214 |
|
215 const char *vsOffsetName, *fsOffsetName; |
|
216 const char *vsRadiiName, *fsRadiiName; |
|
217 |
|
218 builder->addVarying(kVec2f_GrSLType, "EllipseOffsets", &vsOffsetName, &fsOffsetName); |
|
219 const SkString* attr0Name = |
|
220 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]); |
|
221 builder->vsCodeAppendf("\t%s = %s;\n", vsOffsetName, attr0Name->c_str()); |
|
222 |
|
223 builder->addVarying(kVec4f_GrSLType, "EllipseRadii", &vsRadiiName, &fsRadiiName); |
|
224 const SkString* attr1Name = |
|
225 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[1]); |
|
226 builder->vsCodeAppendf("\t%s = %s;\n", vsRadiiName, attr1Name->c_str()); |
|
227 |
|
228 // for outer curve |
|
229 builder->fsCodeAppendf("\tvec2 scaledOffset = %s*%s.xy;\n", fsOffsetName, fsRadiiName); |
|
230 builder->fsCodeAppend("\tfloat test = dot(scaledOffset, scaledOffset) - 1.0;\n"); |
|
231 builder->fsCodeAppendf("\tvec2 grad = 2.0*scaledOffset*%s.xy;\n", fsRadiiName); |
|
232 builder->fsCodeAppend("\tfloat grad_dot = dot(grad, grad);\n"); |
|
233 // we need to clamp the length^2 of the gradiant vector to a non-zero value, because |
|
234 // on the Nexus 4 the undefined result of inversesqrt(0) drops out an entire tile |
|
235 // TODO: restrict this to Adreno-only |
|
236 builder->fsCodeAppend("\tgrad_dot = max(grad_dot, 1.0e-4);\n"); |
|
237 builder->fsCodeAppend("\tfloat invlen = inversesqrt(grad_dot);\n"); |
|
238 builder->fsCodeAppend("\tfloat edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);\n"); |
|
239 |
|
240 // for inner curve |
|
241 if (ellipseEffect.isStroked()) { |
|
242 builder->fsCodeAppendf("\tscaledOffset = %s*%s.zw;\n", fsOffsetName, fsRadiiName); |
|
243 builder->fsCodeAppend("\ttest = dot(scaledOffset, scaledOffset) - 1.0;\n"); |
|
244 builder->fsCodeAppendf("\tgrad = 2.0*scaledOffset*%s.zw;\n", fsRadiiName); |
|
245 builder->fsCodeAppend("\tinvlen = inversesqrt(dot(grad, grad));\n"); |
|
246 builder->fsCodeAppend("\tedgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);\n"); |
|
247 } |
|
248 |
|
249 builder->fsCodeAppendf("\t%s = %s;\n", outputColor, |
|
250 (GrGLSLExpr4(inputColor) * GrGLSLExpr1("edgeAlpha")).c_str()); |
|
251 } |
|
252 |
|
253 static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) { |
|
254 const EllipseEdgeEffect& ellipseEffect = drawEffect.castEffect<EllipseEdgeEffect>(); |
|
255 |
|
256 return ellipseEffect.isStroked() ? 0x1 : 0x0; |
|
257 } |
|
258 |
|
259 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE { |
|
260 } |
|
261 |
|
262 private: |
|
263 typedef GrGLVertexEffect INHERITED; |
|
264 }; |
|
265 |
|
266 private: |
|
267 EllipseEdgeEffect(bool stroke) : GrVertexEffect() { |
|
268 this->addVertexAttrib(kVec2f_GrSLType); |
|
269 this->addVertexAttrib(kVec4f_GrSLType); |
|
270 fStroke = stroke; |
|
271 } |
|
272 |
|
273 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE { |
|
274 const EllipseEdgeEffect& eee = CastEffect<EllipseEdgeEffect>(other); |
|
275 return eee.fStroke == fStroke; |
|
276 } |
|
277 |
|
278 bool fStroke; |
|
279 |
|
280 GR_DECLARE_EFFECT_TEST; |
|
281 |
|
282 typedef GrVertexEffect INHERITED; |
|
283 }; |
|
284 |
|
285 GR_DEFINE_EFFECT_TEST(EllipseEdgeEffect); |
|
286 |
|
287 GrEffectRef* EllipseEdgeEffect::TestCreate(SkRandom* random, |
|
288 GrContext* context, |
|
289 const GrDrawTargetCaps&, |
|
290 GrTexture* textures[]) { |
|
291 return EllipseEdgeEffect::Create(random->nextBool()); |
|
292 } |
|
293 |
|
294 /////////////////////////////////////////////////////////////////////////////// |
|
295 |
|
296 /** |
|
297 * The output of this effect is a modulation of the input color and coverage for an ellipse, |
|
298 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The |
|
299 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by |
|
300 * using differentials. |
|
301 * |
|
302 * The result is device-independent and can be used with any affine matrix. |
|
303 */ |
|
304 |
|
305 class DIEllipseEdgeEffect : public GrVertexEffect { |
|
306 public: |
|
307 enum Mode { kStroke = 0, kHairline, kFill }; |
|
308 |
|
309 static GrEffectRef* Create(Mode mode) { |
|
310 GR_CREATE_STATIC_EFFECT(gEllipseStrokeEdge, DIEllipseEdgeEffect, (kStroke)); |
|
311 GR_CREATE_STATIC_EFFECT(gEllipseHairlineEdge, DIEllipseEdgeEffect, (kHairline)); |
|
312 GR_CREATE_STATIC_EFFECT(gEllipseFillEdge, DIEllipseEdgeEffect, (kFill)); |
|
313 |
|
314 if (kStroke == mode) { |
|
315 gEllipseStrokeEdge->ref(); |
|
316 return gEllipseStrokeEdge; |
|
317 } else if (kHairline == mode) { |
|
318 gEllipseHairlineEdge->ref(); |
|
319 return gEllipseHairlineEdge; |
|
320 } else { |
|
321 gEllipseFillEdge->ref(); |
|
322 return gEllipseFillEdge; |
|
323 } |
|
324 } |
|
325 |
|
326 virtual void getConstantColorComponents(GrColor* color, |
|
327 uint32_t* validFlags) const SK_OVERRIDE { |
|
328 *validFlags = 0; |
|
329 } |
|
330 |
|
331 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE { |
|
332 return GrTBackendEffectFactory<DIEllipseEdgeEffect>::getInstance(); |
|
333 } |
|
334 |
|
335 virtual ~DIEllipseEdgeEffect() {} |
|
336 |
|
337 static const char* Name() { return "DIEllipseEdge"; } |
|
338 |
|
339 inline Mode getMode() const { return fMode; } |
|
340 |
|
341 class GLEffect : public GrGLVertexEffect { |
|
342 public: |
|
343 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&) |
|
344 : INHERITED (factory) {} |
|
345 |
|
346 virtual void emitCode(GrGLFullShaderBuilder* builder, |
|
347 const GrDrawEffect& drawEffect, |
|
348 EffectKey key, |
|
349 const char* outputColor, |
|
350 const char* inputColor, |
|
351 const TransformedCoordsArray&, |
|
352 const TextureSamplerArray& samplers) SK_OVERRIDE { |
|
353 const DIEllipseEdgeEffect& ellipseEffect = drawEffect.castEffect<DIEllipseEdgeEffect>(); |
|
354 |
|
355 SkAssertResult(builder->enableFeature( |
|
356 GrGLShaderBuilder::kStandardDerivatives_GLSLFeature)); |
|
357 |
|
358 const char *vsOffsetName0, *fsOffsetName0; |
|
359 builder->addVarying(kVec2f_GrSLType, "EllipseOffsets0", |
|
360 &vsOffsetName0, &fsOffsetName0); |
|
361 const SkString* attr0Name = |
|
362 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]); |
|
363 builder->vsCodeAppendf("\t%s = %s;\n", vsOffsetName0, attr0Name->c_str()); |
|
364 const char *vsOffsetName1, *fsOffsetName1; |
|
365 builder->addVarying(kVec2f_GrSLType, "EllipseOffsets1", |
|
366 &vsOffsetName1, &fsOffsetName1); |
|
367 const SkString* attr1Name = |
|
368 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[1]); |
|
369 builder->vsCodeAppendf("\t%s = %s;\n", vsOffsetName1, attr1Name->c_str()); |
|
370 |
|
371 // for outer curve |
|
372 builder->fsCodeAppendf("\tvec2 scaledOffset = %s.xy;\n", fsOffsetName0); |
|
373 builder->fsCodeAppend("\tfloat test = dot(scaledOffset, scaledOffset) - 1.0;\n"); |
|
374 builder->fsCodeAppendf("\tvec2 duvdx = dFdx(%s);\n", fsOffsetName0); |
|
375 builder->fsCodeAppendf("\tvec2 duvdy = dFdy(%s);\n", fsOffsetName0); |
|
376 builder->fsCodeAppendf("\tvec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,\n" |
|
377 "\t 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);\n", |
|
378 fsOffsetName0, fsOffsetName0, fsOffsetName0, fsOffsetName0); |
|
379 |
|
380 builder->fsCodeAppend("\tfloat grad_dot = dot(grad, grad);\n"); |
|
381 // we need to clamp the length^2 of the gradiant vector to a non-zero value, because |
|
382 // on the Nexus 4 the undefined result of inversesqrt(0) drops out an entire tile |
|
383 // TODO: restrict this to Adreno-only |
|
384 builder->fsCodeAppend("\tgrad_dot = max(grad_dot, 1.0e-4);\n"); |
|
385 builder->fsCodeAppend("\tfloat invlen = inversesqrt(grad_dot);\n"); |
|
386 if (kHairline == ellipseEffect.getMode()) { |
|
387 // can probably do this with one step |
|
388 builder->fsCodeAppend("\tfloat edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);\n"); |
|
389 builder->fsCodeAppend("\tedgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);\n"); |
|
390 } else { |
|
391 builder->fsCodeAppend("\tfloat edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);\n"); |
|
392 } |
|
393 |
|
394 // for inner curve |
|
395 if (kStroke == ellipseEffect.getMode()) { |
|
396 builder->fsCodeAppendf("\tscaledOffset = %s.xy;\n", fsOffsetName1); |
|
397 builder->fsCodeAppend("\ttest = dot(scaledOffset, scaledOffset) - 1.0;\n"); |
|
398 builder->fsCodeAppendf("\tduvdx = dFdx(%s);\n", fsOffsetName1); |
|
399 builder->fsCodeAppendf("\tduvdy = dFdy(%s);\n", fsOffsetName1); |
|
400 builder->fsCodeAppendf("\tgrad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,\n" |
|
401 "\t 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);\n", |
|
402 fsOffsetName1, fsOffsetName1, fsOffsetName1, fsOffsetName1); |
|
403 builder->fsCodeAppend("\tinvlen = inversesqrt(dot(grad, grad));\n"); |
|
404 builder->fsCodeAppend("\tedgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);\n"); |
|
405 } |
|
406 |
|
407 builder->fsCodeAppendf("\t%s = %s;\n", outputColor, |
|
408 (GrGLSLExpr4(inputColor) * GrGLSLExpr1("edgeAlpha")).c_str()); |
|
409 } |
|
410 |
|
411 static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) { |
|
412 const DIEllipseEdgeEffect& ellipseEffect = drawEffect.castEffect<DIEllipseEdgeEffect>(); |
|
413 |
|
414 return ellipseEffect.getMode(); |
|
415 } |
|
416 |
|
417 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE { |
|
418 } |
|
419 |
|
420 private: |
|
421 typedef GrGLVertexEffect INHERITED; |
|
422 }; |
|
423 |
|
424 private: |
|
425 DIEllipseEdgeEffect(Mode mode) : GrVertexEffect() { |
|
426 this->addVertexAttrib(kVec2f_GrSLType); |
|
427 this->addVertexAttrib(kVec2f_GrSLType); |
|
428 fMode = mode; |
|
429 } |
|
430 |
|
431 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE { |
|
432 const DIEllipseEdgeEffect& eee = CastEffect<DIEllipseEdgeEffect>(other); |
|
433 return eee.fMode == fMode; |
|
434 } |
|
435 |
|
436 Mode fMode; |
|
437 |
|
438 GR_DECLARE_EFFECT_TEST; |
|
439 |
|
440 typedef GrVertexEffect INHERITED; |
|
441 }; |
|
442 |
|
443 GR_DEFINE_EFFECT_TEST(DIEllipseEdgeEffect); |
|
444 |
|
445 GrEffectRef* DIEllipseEdgeEffect::TestCreate(SkRandom* random, |
|
446 GrContext* context, |
|
447 const GrDrawTargetCaps&, |
|
448 GrTexture* textures[]) { |
|
449 return DIEllipseEdgeEffect::Create((Mode)(random->nextRangeU(0,2))); |
|
450 } |
|
451 |
|
452 /////////////////////////////////////////////////////////////////////////////// |
|
453 |
|
454 void GrOvalRenderer::reset() { |
|
455 SkSafeSetNull(fRRectIndexBuffer); |
|
456 } |
|
457 |
|
458 bool GrOvalRenderer::drawOval(GrDrawTarget* target, const GrContext* context, bool useAA, |
|
459 const SkRect& oval, const SkStrokeRec& stroke) |
|
460 { |
|
461 bool useCoverageAA = useAA && |
|
462 !target->getDrawState().getRenderTarget()->isMultisampled() && |
|
463 !target->shouldDisableCoverageAAForBlend(); |
|
464 |
|
465 if (!useCoverageAA) { |
|
466 return false; |
|
467 } |
|
468 |
|
469 const SkMatrix& vm = context->getMatrix(); |
|
470 |
|
471 // we can draw circles |
|
472 if (SkScalarNearlyEqual(oval.width(), oval.height()) |
|
473 && circle_stays_circle(vm)) { |
|
474 this->drawCircle(target, useCoverageAA, oval, stroke); |
|
475 // if we have shader derivative support, render as device-independent |
|
476 } else if (target->caps()->shaderDerivativeSupport()) { |
|
477 return this->drawDIEllipse(target, useCoverageAA, oval, stroke); |
|
478 // otherwise axis-aligned ellipses only |
|
479 } else if (vm.rectStaysRect()) { |
|
480 return this->drawEllipse(target, useCoverageAA, oval, stroke); |
|
481 } else { |
|
482 return false; |
|
483 } |
|
484 |
|
485 return true; |
|
486 } |
|
487 |
|
488 /////////////////////////////////////////////////////////////////////////////// |
|
489 |
|
490 // position + edge |
|
491 extern const GrVertexAttrib gCircleVertexAttribs[] = { |
|
492 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding}, |
|
493 {kVec4f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding} |
|
494 }; |
|
495 |
|
496 void GrOvalRenderer::drawCircle(GrDrawTarget* target, |
|
497 bool useCoverageAA, |
|
498 const SkRect& circle, |
|
499 const SkStrokeRec& stroke) |
|
500 { |
|
501 GrDrawState* drawState = target->drawState(); |
|
502 |
|
503 const SkMatrix& vm = drawState->getViewMatrix(); |
|
504 GrPoint center = GrPoint::Make(circle.centerX(), circle.centerY()); |
|
505 vm.mapPoints(¢er, 1); |
|
506 SkScalar radius = vm.mapRadius(SkScalarHalf(circle.width())); |
|
507 SkScalar strokeWidth = vm.mapRadius(stroke.getWidth()); |
|
508 |
|
509 GrDrawState::AutoViewMatrixRestore avmr; |
|
510 if (!avmr.setIdentity(drawState)) { |
|
511 return; |
|
512 } |
|
513 |
|
514 drawState->setVertexAttribs<gCircleVertexAttribs>(SK_ARRAY_COUNT(gCircleVertexAttribs)); |
|
515 SkASSERT(sizeof(CircleVertex) == drawState->getVertexSize()); |
|
516 |
|
517 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0); |
|
518 if (!geo.succeeded()) { |
|
519 GrPrintf("Failed to get space for vertices!\n"); |
|
520 return; |
|
521 } |
|
522 |
|
523 CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices()); |
|
524 |
|
525 SkStrokeRec::Style style = stroke.getStyle(); |
|
526 bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style); |
|
527 |
|
528 SkScalar innerRadius = 0.0f; |
|
529 SkScalar outerRadius = radius; |
|
530 SkScalar halfWidth = 0; |
|
531 if (style != SkStrokeRec::kFill_Style) { |
|
532 if (SkScalarNearlyZero(strokeWidth)) { |
|
533 halfWidth = SK_ScalarHalf; |
|
534 } else { |
|
535 halfWidth = SkScalarHalf(strokeWidth); |
|
536 } |
|
537 |
|
538 outerRadius += halfWidth; |
|
539 if (isStroked) { |
|
540 innerRadius = radius - halfWidth; |
|
541 } |
|
542 } |
|
543 |
|
544 GrEffectRef* effect = CircleEdgeEffect::Create(isStroked && innerRadius > 0); |
|
545 static const int kCircleEdgeAttrIndex = 1; |
|
546 drawState->addCoverageEffect(effect, kCircleEdgeAttrIndex)->unref(); |
|
547 |
|
548 // The radii are outset for two reasons. First, it allows the shader to simply perform |
|
549 // clamp(distance-to-center - radius, 0, 1). Second, the outer radius is used to compute the |
|
550 // verts of the bounding box that is rendered and the outset ensures the box will cover all |
|
551 // pixels partially covered by the circle. |
|
552 outerRadius += SK_ScalarHalf; |
|
553 innerRadius -= SK_ScalarHalf; |
|
554 |
|
555 SkRect bounds = SkRect::MakeLTRB( |
|
556 center.fX - outerRadius, |
|
557 center.fY - outerRadius, |
|
558 center.fX + outerRadius, |
|
559 center.fY + outerRadius |
|
560 ); |
|
561 |
|
562 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop); |
|
563 verts[0].fOffset = SkPoint::Make(-outerRadius, -outerRadius); |
|
564 verts[0].fOuterRadius = outerRadius; |
|
565 verts[0].fInnerRadius = innerRadius; |
|
566 |
|
567 verts[1].fPos = SkPoint::Make(bounds.fRight, bounds.fTop); |
|
568 verts[1].fOffset = SkPoint::Make(outerRadius, -outerRadius); |
|
569 verts[1].fOuterRadius = outerRadius; |
|
570 verts[1].fInnerRadius = innerRadius; |
|
571 |
|
572 verts[2].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom); |
|
573 verts[2].fOffset = SkPoint::Make(-outerRadius, outerRadius); |
|
574 verts[2].fOuterRadius = outerRadius; |
|
575 verts[2].fInnerRadius = innerRadius; |
|
576 |
|
577 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom); |
|
578 verts[3].fOffset = SkPoint::Make(outerRadius, outerRadius); |
|
579 verts[3].fOuterRadius = outerRadius; |
|
580 verts[3].fInnerRadius = innerRadius; |
|
581 |
|
582 target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4, &bounds); |
|
583 } |
|
584 |
|
585 /////////////////////////////////////////////////////////////////////////////// |
|
586 |
|
587 // position + offset + 1/radii |
|
588 extern const GrVertexAttrib gEllipseVertexAttribs[] = { |
|
589 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding}, |
|
590 {kVec2f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding}, |
|
591 {kVec4f_GrVertexAttribType, 2*sizeof(GrPoint), kEffect_GrVertexAttribBinding} |
|
592 }; |
|
593 |
|
594 // position + offsets |
|
595 extern const GrVertexAttrib gDIEllipseVertexAttribs[] = { |
|
596 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding}, |
|
597 {kVec2f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding}, |
|
598 {kVec2f_GrVertexAttribType, 2*sizeof(GrPoint), kEffect_GrVertexAttribBinding}, |
|
599 }; |
|
600 |
|
601 bool GrOvalRenderer::drawEllipse(GrDrawTarget* target, |
|
602 bool useCoverageAA, |
|
603 const SkRect& ellipse, |
|
604 const SkStrokeRec& stroke) |
|
605 { |
|
606 GrDrawState* drawState = target->drawState(); |
|
607 #ifdef SK_DEBUG |
|
608 { |
|
609 // we should have checked for this previously |
|
610 bool isAxisAlignedEllipse = drawState->getViewMatrix().rectStaysRect(); |
|
611 SkASSERT(useCoverageAA && isAxisAlignedEllipse); |
|
612 } |
|
613 #endif |
|
614 |
|
615 // do any matrix crunching before we reset the draw state for device coords |
|
616 const SkMatrix& vm = drawState->getViewMatrix(); |
|
617 GrPoint center = GrPoint::Make(ellipse.centerX(), ellipse.centerY()); |
|
618 vm.mapPoints(¢er, 1); |
|
619 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width()); |
|
620 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height()); |
|
621 SkScalar xRadius = SkScalarAbs(vm[SkMatrix::kMScaleX]*ellipseXRadius + |
|
622 vm[SkMatrix::kMSkewY]*ellipseYRadius); |
|
623 SkScalar yRadius = SkScalarAbs(vm[SkMatrix::kMSkewX]*ellipseXRadius + |
|
624 vm[SkMatrix::kMScaleY]*ellipseYRadius); |
|
625 |
|
626 // do (potentially) anisotropic mapping of stroke |
|
627 SkVector scaledStroke; |
|
628 SkScalar strokeWidth = stroke.getWidth(); |
|
629 scaledStroke.fX = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMScaleX] + vm[SkMatrix::kMSkewY])); |
|
630 scaledStroke.fY = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMSkewX] + vm[SkMatrix::kMScaleY])); |
|
631 |
|
632 SkStrokeRec::Style style = stroke.getStyle(); |
|
633 bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style); |
|
634 |
|
635 SkScalar innerXRadius = 0; |
|
636 SkScalar innerYRadius = 0; |
|
637 if (SkStrokeRec::kFill_Style != style) { |
|
638 if (SkScalarNearlyZero(scaledStroke.length())) { |
|
639 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf); |
|
640 } else { |
|
641 scaledStroke.scale(SK_ScalarHalf); |
|
642 } |
|
643 |
|
644 // we only handle thick strokes for near-circular ellipses |
|
645 if (scaledStroke.length() > SK_ScalarHalf && |
|
646 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) { |
|
647 return false; |
|
648 } |
|
649 |
|
650 // we don't handle it if curvature of the stroke is less than curvature of the ellipse |
|
651 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius || |
|
652 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) { |
|
653 return false; |
|
654 } |
|
655 |
|
656 // this is legit only if scale & translation (which should be the case at the moment) |
|
657 if (isStroked) { |
|
658 innerXRadius = xRadius - scaledStroke.fX; |
|
659 innerYRadius = yRadius - scaledStroke.fY; |
|
660 } |
|
661 |
|
662 xRadius += scaledStroke.fX; |
|
663 yRadius += scaledStroke.fY; |
|
664 } |
|
665 |
|
666 GrDrawState::AutoViewMatrixRestore avmr; |
|
667 if (!avmr.setIdentity(drawState)) { |
|
668 return false; |
|
669 } |
|
670 |
|
671 drawState->setVertexAttribs<gEllipseVertexAttribs>(SK_ARRAY_COUNT(gEllipseVertexAttribs)); |
|
672 SkASSERT(sizeof(EllipseVertex) == drawState->getVertexSize()); |
|
673 |
|
674 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0); |
|
675 if (!geo.succeeded()) { |
|
676 GrPrintf("Failed to get space for vertices!\n"); |
|
677 return false; |
|
678 } |
|
679 |
|
680 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices()); |
|
681 |
|
682 GrEffectRef* effect = EllipseEdgeEffect::Create(isStroked && |
|
683 innerXRadius > 0 && innerYRadius > 0); |
|
684 |
|
685 static const int kEllipseCenterAttrIndex = 1; |
|
686 static const int kEllipseEdgeAttrIndex = 2; |
|
687 drawState->addCoverageEffect(effect, kEllipseCenterAttrIndex, kEllipseEdgeAttrIndex)->unref(); |
|
688 |
|
689 // Compute the reciprocals of the radii here to save time in the shader |
|
690 SkScalar xRadRecip = SkScalarInvert(xRadius); |
|
691 SkScalar yRadRecip = SkScalarInvert(yRadius); |
|
692 SkScalar xInnerRadRecip = SkScalarInvert(innerXRadius); |
|
693 SkScalar yInnerRadRecip = SkScalarInvert(innerYRadius); |
|
694 |
|
695 // We've extended the outer x radius out half a pixel to antialias. |
|
696 // This will also expand the rect so all the pixels will be captured. |
|
697 // TODO: Consider if we should use sqrt(2)/2 instead |
|
698 xRadius += SK_ScalarHalf; |
|
699 yRadius += SK_ScalarHalf; |
|
700 |
|
701 SkRect bounds = SkRect::MakeLTRB( |
|
702 center.fX - xRadius, |
|
703 center.fY - yRadius, |
|
704 center.fX + xRadius, |
|
705 center.fY + yRadius |
|
706 ); |
|
707 |
|
708 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop); |
|
709 verts[0].fOffset = SkPoint::Make(-xRadius, -yRadius); |
|
710 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); |
|
711 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); |
|
712 |
|
713 verts[1].fPos = SkPoint::Make(bounds.fRight, bounds.fTop); |
|
714 verts[1].fOffset = SkPoint::Make(xRadius, -yRadius); |
|
715 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); |
|
716 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); |
|
717 |
|
718 verts[2].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom); |
|
719 verts[2].fOffset = SkPoint::Make(-xRadius, yRadius); |
|
720 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); |
|
721 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); |
|
722 |
|
723 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom); |
|
724 verts[3].fOffset = SkPoint::Make(xRadius, yRadius); |
|
725 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); |
|
726 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); |
|
727 |
|
728 target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4, &bounds); |
|
729 |
|
730 return true; |
|
731 } |
|
732 |
|
733 bool GrOvalRenderer::drawDIEllipse(GrDrawTarget* target, |
|
734 bool useCoverageAA, |
|
735 const SkRect& ellipse, |
|
736 const SkStrokeRec& stroke) |
|
737 { |
|
738 GrDrawState* drawState = target->drawState(); |
|
739 const SkMatrix& vm = drawState->getViewMatrix(); |
|
740 |
|
741 GrPoint center = GrPoint::Make(ellipse.centerX(), ellipse.centerY()); |
|
742 SkScalar xRadius = SkScalarHalf(ellipse.width()); |
|
743 SkScalar yRadius = SkScalarHalf(ellipse.height()); |
|
744 |
|
745 SkStrokeRec::Style style = stroke.getStyle(); |
|
746 DIEllipseEdgeEffect::Mode mode = (SkStrokeRec::kStroke_Style == style) ? |
|
747 DIEllipseEdgeEffect::kStroke : |
|
748 (SkStrokeRec::kHairline_Style == style) ? |
|
749 DIEllipseEdgeEffect::kHairline : DIEllipseEdgeEffect::kFill; |
|
750 |
|
751 SkScalar innerXRadius = 0; |
|
752 SkScalar innerYRadius = 0; |
|
753 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) { |
|
754 SkScalar strokeWidth = stroke.getWidth(); |
|
755 |
|
756 if (SkScalarNearlyZero(strokeWidth)) { |
|
757 strokeWidth = SK_ScalarHalf; |
|
758 } else { |
|
759 strokeWidth *= SK_ScalarHalf; |
|
760 } |
|
761 |
|
762 // we only handle thick strokes for near-circular ellipses |
|
763 if (strokeWidth > SK_ScalarHalf && |
|
764 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) { |
|
765 return false; |
|
766 } |
|
767 |
|
768 // we don't handle it if curvature of the stroke is less than curvature of the ellipse |
|
769 if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius || |
|
770 strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) { |
|
771 return false; |
|
772 } |
|
773 |
|
774 // set inner radius (if needed) |
|
775 if (SkStrokeRec::kStroke_Style == style) { |
|
776 innerXRadius = xRadius - strokeWidth; |
|
777 innerYRadius = yRadius - strokeWidth; |
|
778 } |
|
779 |
|
780 xRadius += strokeWidth; |
|
781 yRadius += strokeWidth; |
|
782 } |
|
783 if (DIEllipseEdgeEffect::kStroke == mode) { |
|
784 mode = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseEdgeEffect::kStroke : |
|
785 DIEllipseEdgeEffect::kFill; |
|
786 } |
|
787 SkScalar innerRatioX = SkScalarDiv(xRadius, innerXRadius); |
|
788 SkScalar innerRatioY = SkScalarDiv(yRadius, innerYRadius); |
|
789 |
|
790 drawState->setVertexAttribs<gDIEllipseVertexAttribs>(SK_ARRAY_COUNT(gDIEllipseVertexAttribs)); |
|
791 SkASSERT(sizeof(DIEllipseVertex) == drawState->getVertexSize()); |
|
792 |
|
793 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0); |
|
794 if (!geo.succeeded()) { |
|
795 GrPrintf("Failed to get space for vertices!\n"); |
|
796 return false; |
|
797 } |
|
798 |
|
799 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(geo.vertices()); |
|
800 |
|
801 GrEffectRef* effect = DIEllipseEdgeEffect::Create(mode); |
|
802 |
|
803 static const int kEllipseOuterOffsetAttrIndex = 1; |
|
804 static const int kEllipseInnerOffsetAttrIndex = 2; |
|
805 drawState->addCoverageEffect(effect, kEllipseOuterOffsetAttrIndex, |
|
806 kEllipseInnerOffsetAttrIndex)->unref(); |
|
807 |
|
808 // This expands the outer rect so that after CTM we end up with a half-pixel border |
|
809 SkScalar a = vm[SkMatrix::kMScaleX]; |
|
810 SkScalar b = vm[SkMatrix::kMSkewX]; |
|
811 SkScalar c = vm[SkMatrix::kMSkewY]; |
|
812 SkScalar d = vm[SkMatrix::kMScaleY]; |
|
813 SkScalar geoDx = SkScalarDiv(SK_ScalarHalf, SkScalarSqrt(a*a + c*c)); |
|
814 SkScalar geoDy = SkScalarDiv(SK_ScalarHalf, SkScalarSqrt(b*b + d*d)); |
|
815 // This adjusts the "radius" to include the half-pixel border |
|
816 SkScalar offsetDx = SkScalarDiv(geoDx, xRadius); |
|
817 SkScalar offsetDy = SkScalarDiv(geoDy, yRadius); |
|
818 |
|
819 SkRect bounds = SkRect::MakeLTRB( |
|
820 center.fX - xRadius - geoDx, |
|
821 center.fY - yRadius - geoDy, |
|
822 center.fX + xRadius + geoDx, |
|
823 center.fY + yRadius + geoDy |
|
824 ); |
|
825 |
|
826 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop); |
|
827 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy); |
|
828 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy); |
|
829 |
|
830 verts[1].fPos = SkPoint::Make(bounds.fRight, bounds.fTop); |
|
831 verts[1].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy); |
|
832 verts[1].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy); |
|
833 |
|
834 verts[2].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom); |
|
835 verts[2].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy); |
|
836 verts[2].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy); |
|
837 |
|
838 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom); |
|
839 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy); |
|
840 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy); |
|
841 |
|
842 target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4, &bounds); |
|
843 |
|
844 return true; |
|
845 } |
|
846 |
|
847 /////////////////////////////////////////////////////////////////////////////// |
|
848 |
|
849 static const uint16_t gRRectIndices[] = { |
|
850 // corners |
|
851 0, 1, 5, 0, 5, 4, |
|
852 2, 3, 7, 2, 7, 6, |
|
853 8, 9, 13, 8, 13, 12, |
|
854 10, 11, 15, 10, 15, 14, |
|
855 |
|
856 // edges |
|
857 1, 2, 6, 1, 6, 5, |
|
858 4, 5, 9, 4, 9, 8, |
|
859 6, 7, 11, 6, 11, 10, |
|
860 9, 10, 14, 9, 14, 13, |
|
861 |
|
862 // center |
|
863 // we place this at the end so that we can ignore these indices when rendering stroke-only |
|
864 5, 6, 10, 5, 10, 9 |
|
865 }; |
|
866 |
|
867 |
|
868 GrIndexBuffer* GrOvalRenderer::rRectIndexBuffer(GrGpu* gpu) { |
|
869 if (NULL == fRRectIndexBuffer) { |
|
870 fRRectIndexBuffer = |
|
871 gpu->createIndexBuffer(sizeof(gRRectIndices), false); |
|
872 if (NULL != fRRectIndexBuffer) { |
|
873 #ifdef SK_DEBUG |
|
874 bool updated = |
|
875 #endif |
|
876 fRRectIndexBuffer->updateData(gRRectIndices, |
|
877 sizeof(gRRectIndices)); |
|
878 GR_DEBUGASSERT(updated); |
|
879 } |
|
880 } |
|
881 return fRRectIndexBuffer; |
|
882 } |
|
883 |
|
884 bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, bool useAA, |
|
885 const SkRRect& rrect, const SkStrokeRec& stroke) |
|
886 { |
|
887 bool useCoverageAA = useAA && |
|
888 !target->getDrawState().getRenderTarget()->isMultisampled() && |
|
889 !target->shouldDisableCoverageAAForBlend(); |
|
890 |
|
891 // only anti-aliased rrects for now |
|
892 if (!useCoverageAA) { |
|
893 return false; |
|
894 } |
|
895 |
|
896 const SkMatrix& vm = context->getMatrix(); |
|
897 #ifdef SK_DEBUG |
|
898 { |
|
899 // we should have checked for this previously |
|
900 SkASSERT(useCoverageAA && vm.rectStaysRect() && rrect.isSimple()); |
|
901 } |
|
902 #endif |
|
903 |
|
904 // do any matrix crunching before we reset the draw state for device coords |
|
905 const SkRect& rrectBounds = rrect.getBounds(); |
|
906 SkRect bounds; |
|
907 vm.mapRect(&bounds, rrectBounds); |
|
908 |
|
909 SkVector radii = rrect.getSimpleRadii(); |
|
910 SkScalar xRadius = SkScalarAbs(vm[SkMatrix::kMScaleX]*radii.fX + |
|
911 vm[SkMatrix::kMSkewY]*radii.fY); |
|
912 SkScalar yRadius = SkScalarAbs(vm[SkMatrix::kMSkewX]*radii.fX + |
|
913 vm[SkMatrix::kMScaleY]*radii.fY); |
|
914 |
|
915 // if hairline stroke is greater than radius, we don't handle that right now |
|
916 SkStrokeRec::Style style = stroke.getStyle(); |
|
917 if (SkStrokeRec::kHairline_Style == style && |
|
918 (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) { |
|
919 return false; |
|
920 } |
|
921 |
|
922 // do (potentially) anisotropic mapping of stroke |
|
923 SkVector scaledStroke; |
|
924 SkScalar strokeWidth = stroke.getWidth(); |
|
925 scaledStroke.fX = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMScaleX] + vm[SkMatrix::kMSkewY])); |
|
926 scaledStroke.fY = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMSkewX] + vm[SkMatrix::kMScaleY])); |
|
927 |
|
928 // if half of strokewidth is greater than radius, we don't handle that right now |
|
929 if (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius) { |
|
930 return false; |
|
931 } |
|
932 |
|
933 // reset to device coordinates |
|
934 GrDrawState* drawState = target->drawState(); |
|
935 GrDrawState::AutoViewMatrixRestore avmr; |
|
936 if (!avmr.setIdentity(drawState)) { |
|
937 return false; |
|
938 } |
|
939 |
|
940 bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style); |
|
941 |
|
942 GrIndexBuffer* indexBuffer = this->rRectIndexBuffer(context->getGpu()); |
|
943 if (NULL == indexBuffer) { |
|
944 GrPrintf("Failed to create index buffer!\n"); |
|
945 return false; |
|
946 } |
|
947 |
|
948 // if the corners are circles, use the circle renderer |
|
949 if ((!isStroked || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) { |
|
950 drawState->setVertexAttribs<gCircleVertexAttribs>(SK_ARRAY_COUNT(gCircleVertexAttribs)); |
|
951 SkASSERT(sizeof(CircleVertex) == drawState->getVertexSize()); |
|
952 |
|
953 GrDrawTarget::AutoReleaseGeometry geo(target, 16, 0); |
|
954 if (!geo.succeeded()) { |
|
955 GrPrintf("Failed to get space for vertices!\n"); |
|
956 return false; |
|
957 } |
|
958 CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices()); |
|
959 |
|
960 SkScalar innerRadius = 0.0f; |
|
961 SkScalar outerRadius = xRadius; |
|
962 SkScalar halfWidth = 0; |
|
963 if (style != SkStrokeRec::kFill_Style) { |
|
964 if (SkScalarNearlyZero(scaledStroke.fX)) { |
|
965 halfWidth = SK_ScalarHalf; |
|
966 } else { |
|
967 halfWidth = SkScalarHalf(scaledStroke.fX); |
|
968 } |
|
969 |
|
970 if (isStroked) { |
|
971 innerRadius = xRadius - halfWidth; |
|
972 } |
|
973 outerRadius += halfWidth; |
|
974 bounds.outset(halfWidth, halfWidth); |
|
975 } |
|
976 |
|
977 isStroked = (isStroked && innerRadius >= 0); |
|
978 |
|
979 GrEffectRef* effect = CircleEdgeEffect::Create(isStroked); |
|
980 static const int kCircleEdgeAttrIndex = 1; |
|
981 drawState->addCoverageEffect(effect, kCircleEdgeAttrIndex)->unref(); |
|
982 |
|
983 // The radii are outset for two reasons. First, it allows the shader to simply perform |
|
984 // clamp(distance-to-center - radius, 0, 1). Second, the outer radius is used to compute the |
|
985 // verts of the bounding box that is rendered and the outset ensures the box will cover all |
|
986 // pixels partially covered by the circle. |
|
987 outerRadius += SK_ScalarHalf; |
|
988 innerRadius -= SK_ScalarHalf; |
|
989 |
|
990 // Expand the rect so all the pixels will be captured. |
|
991 bounds.outset(SK_ScalarHalf, SK_ScalarHalf); |
|
992 |
|
993 SkScalar yCoords[4] = { |
|
994 bounds.fTop, |
|
995 bounds.fTop + outerRadius, |
|
996 bounds.fBottom - outerRadius, |
|
997 bounds.fBottom |
|
998 }; |
|
999 SkScalar yOuterRadii[4] = { |
|
1000 -outerRadius, |
|
1001 0, |
|
1002 0, |
|
1003 outerRadius |
|
1004 }; |
|
1005 for (int i = 0; i < 4; ++i) { |
|
1006 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]); |
|
1007 verts->fOffset = SkPoint::Make(-outerRadius, yOuterRadii[i]); |
|
1008 verts->fOuterRadius = outerRadius; |
|
1009 verts->fInnerRadius = innerRadius; |
|
1010 verts++; |
|
1011 |
|
1012 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]); |
|
1013 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]); |
|
1014 verts->fOuterRadius = outerRadius; |
|
1015 verts->fInnerRadius = innerRadius; |
|
1016 verts++; |
|
1017 |
|
1018 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]); |
|
1019 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]); |
|
1020 verts->fOuterRadius = outerRadius; |
|
1021 verts->fInnerRadius = innerRadius; |
|
1022 verts++; |
|
1023 |
|
1024 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]); |
|
1025 verts->fOffset = SkPoint::Make(outerRadius, yOuterRadii[i]); |
|
1026 verts->fOuterRadius = outerRadius; |
|
1027 verts->fInnerRadius = innerRadius; |
|
1028 verts++; |
|
1029 } |
|
1030 |
|
1031 // drop out the middle quad if we're stroked |
|
1032 int indexCnt = isStroked ? GR_ARRAY_COUNT(gRRectIndices)-6 : GR_ARRAY_COUNT(gRRectIndices); |
|
1033 target->setIndexSourceToBuffer(indexBuffer); |
|
1034 target->drawIndexed(kTriangles_GrPrimitiveType, 0, 0, 16, indexCnt, &bounds); |
|
1035 |
|
1036 // otherwise we use the ellipse renderer |
|
1037 } else { |
|
1038 drawState->setVertexAttribs<gEllipseVertexAttribs>(SK_ARRAY_COUNT(gEllipseVertexAttribs)); |
|
1039 SkASSERT(sizeof(EllipseVertex) == drawState->getVertexSize()); |
|
1040 |
|
1041 SkScalar innerXRadius = 0.0f; |
|
1042 SkScalar innerYRadius = 0.0f; |
|
1043 if (SkStrokeRec::kFill_Style != style) { |
|
1044 if (SkScalarNearlyZero(scaledStroke.length())) { |
|
1045 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf); |
|
1046 } else { |
|
1047 scaledStroke.scale(SK_ScalarHalf); |
|
1048 } |
|
1049 |
|
1050 // we only handle thick strokes for near-circular ellipses |
|
1051 if (scaledStroke.length() > SK_ScalarHalf && |
|
1052 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) { |
|
1053 return false; |
|
1054 } |
|
1055 |
|
1056 // we don't handle it if curvature of the stroke is less than curvature of the ellipse |
|
1057 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius || |
|
1058 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) { |
|
1059 return false; |
|
1060 } |
|
1061 |
|
1062 // this is legit only if scale & translation (which should be the case at the moment) |
|
1063 if (isStroked) { |
|
1064 innerXRadius = xRadius - scaledStroke.fX; |
|
1065 innerYRadius = yRadius - scaledStroke.fY; |
|
1066 } |
|
1067 |
|
1068 xRadius += scaledStroke.fX; |
|
1069 yRadius += scaledStroke.fY; |
|
1070 bounds.outset(scaledStroke.fX, scaledStroke.fY); |
|
1071 } |
|
1072 |
|
1073 isStroked = (isStroked && innerXRadius >= 0 && innerYRadius >= 0); |
|
1074 |
|
1075 GrDrawTarget::AutoReleaseGeometry geo(target, 16, 0); |
|
1076 if (!geo.succeeded()) { |
|
1077 GrPrintf("Failed to get space for vertices!\n"); |
|
1078 return false; |
|
1079 } |
|
1080 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices()); |
|
1081 |
|
1082 GrEffectRef* effect = EllipseEdgeEffect::Create(isStroked); |
|
1083 static const int kEllipseOffsetAttrIndex = 1; |
|
1084 static const int kEllipseRadiiAttrIndex = 2; |
|
1085 drawState->addCoverageEffect(effect, |
|
1086 kEllipseOffsetAttrIndex, kEllipseRadiiAttrIndex)->unref(); |
|
1087 |
|
1088 // Compute the reciprocals of the radii here to save time in the shader |
|
1089 SkScalar xRadRecip = SkScalarInvert(xRadius); |
|
1090 SkScalar yRadRecip = SkScalarInvert(yRadius); |
|
1091 SkScalar xInnerRadRecip = SkScalarInvert(innerXRadius); |
|
1092 SkScalar yInnerRadRecip = SkScalarInvert(innerYRadius); |
|
1093 |
|
1094 // Extend the radii out half a pixel to antialias. |
|
1095 SkScalar xOuterRadius = xRadius + SK_ScalarHalf; |
|
1096 SkScalar yOuterRadius = yRadius + SK_ScalarHalf; |
|
1097 |
|
1098 // Expand the rect so all the pixels will be captured. |
|
1099 bounds.outset(SK_ScalarHalf, SK_ScalarHalf); |
|
1100 |
|
1101 SkScalar yCoords[4] = { |
|
1102 bounds.fTop, |
|
1103 bounds.fTop + yOuterRadius, |
|
1104 bounds.fBottom - yOuterRadius, |
|
1105 bounds.fBottom |
|
1106 }; |
|
1107 SkScalar yOuterOffsets[4] = { |
|
1108 yOuterRadius, |
|
1109 SK_ScalarNearlyZero, // we're using inversesqrt() in the shader, so can't be exactly 0 |
|
1110 SK_ScalarNearlyZero, |
|
1111 yOuterRadius |
|
1112 }; |
|
1113 |
|
1114 for (int i = 0; i < 4; ++i) { |
|
1115 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]); |
|
1116 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]); |
|
1117 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); |
|
1118 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); |
|
1119 verts++; |
|
1120 |
|
1121 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]); |
|
1122 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]); |
|
1123 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); |
|
1124 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); |
|
1125 verts++; |
|
1126 |
|
1127 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]); |
|
1128 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]); |
|
1129 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); |
|
1130 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); |
|
1131 verts++; |
|
1132 |
|
1133 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]); |
|
1134 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]); |
|
1135 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); |
|
1136 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); |
|
1137 verts++; |
|
1138 } |
|
1139 |
|
1140 // drop out the middle quad if we're stroked |
|
1141 int indexCnt = isStroked ? GR_ARRAY_COUNT(gRRectIndices)-6 : GR_ARRAY_COUNT(gRRectIndices); |
|
1142 target->setIndexSourceToBuffer(indexBuffer); |
|
1143 target->drawIndexed(kTriangles_GrPrimitiveType, 0, 0, 16, indexCnt, &bounds); |
|
1144 } |
|
1145 |
|
1146 return true; |
|
1147 } |