blob: 65715f8a5be80ac291e235e3d2067e9a38d2535e [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"
13#include "GrTBackendEffectFactory.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000014
15#include "GrDrawState.h"
16#include "GrDrawTarget.h"
17#include "SkStrokeRec.h"
18
19SK_DEFINE_INST_COUNT(GrOvalRenderer)
20
21namespace {
22
23struct CircleVertex {
24 GrPoint fPos;
25 GrPoint fCenter;
26 SkScalar fOuterRadius;
27 SkScalar fInnerRadius;
28};
29
30struct EllipseVertex {
31 GrPoint fPos;
32 GrPoint fCenter;
33 SkScalar fOuterXRadius;
34 SkScalar fOuterXYRatio;
35 SkScalar fInnerXRadius;
36 SkScalar fInnerXYRatio;
37};
38
39inline bool circle_stays_circle(const SkMatrix& m) {
40 return m.isSimilarity();
41}
42
43}
44
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000045///////////////////////////////////////////////////////////////////////////////
46
47/**
48 * The output of this effect is a modulation of the input color and coverage for a circle,
49 * specified as center_x, center_y, x_radius, inner radius and outer radius in window space
50 * (y-down).
51 */
52
53class CircleEdgeEffect : public GrEffect {
54public:
55 static GrEffectRef* Create(bool stroke) {
56 // we go through this so we only have one copy of each effect (stroked/filled)
57 static SkAutoTUnref<GrEffectRef> gCircleStrokeEdgeEffectRef(
58 CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(CircleEdgeEffect, (true)))));
59 static SkAutoTUnref<GrEffectRef> gCircleFillEdgeEffectRef(
60 CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(CircleEdgeEffect, (false)))));
61
62 if (stroke) {
63 gCircleStrokeEdgeEffectRef.get()->ref();
64 return gCircleStrokeEdgeEffectRef;
65 } else {
66 gCircleFillEdgeEffectRef.get()->ref();
67 return gCircleFillEdgeEffectRef;
68 }
69 }
70
71 virtual void getConstantColorComponents(GrColor* color,
72 uint32_t* validFlags) const SK_OVERRIDE {
73 *validFlags = 0;
74 }
75
76 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
77 return GrTBackendEffectFactory<CircleEdgeEffect>::getInstance();
78 }
79
80 virtual ~CircleEdgeEffect() {}
81
82 static const char* Name() { return "CircleEdge"; }
83
84 inline bool isStroked() const { return fStroke; }
85
86 class GLEffect : public GrGLEffect {
87 public:
88 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
89 : INHERITED (factory) {}
90
91 virtual void emitCode(GrGLShaderBuilder* builder,
92 const GrDrawEffect& drawEffect,
93 EffectKey key,
94 const char* outputColor,
95 const char* inputColor,
96 const TextureSamplerArray& samplers) SK_OVERRIDE {
97 const CircleEdgeEffect& circleEffect = drawEffect.castEffect<CircleEdgeEffect>();
98 const char *vsName, *fsName;
99 builder->addVarying(kVec4f_GrSLType, "CircleEdge", &vsName, &fsName);
100
101 const SkString* attrName =
102 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
103 builder->vsCodeAppendf("\t%s = %s;\n", vsName, attrName->c_str());
104
105 builder->fsCodeAppendf("\tfloat d = distance(%s.xy, %s.xy);\n",
106 builder->fragmentPosition(), fsName);
107 builder->fsCodeAppendf("\tfloat edgeAlpha = clamp(%s.z - d, 0.0, 1.0);\n", fsName);
108 if (circleEffect.isStroked()) {
109 builder->fsCodeAppendf("\tfloat innerAlpha = clamp(d - %s.w, 0.0, 1.0);\n", fsName);
110 builder->fsCodeAppend("\tedgeAlpha *= innerAlpha;\n");
111 }
112 SkString modulate;
113 GrGLSLModulate4f(&modulate, inputColor, "edgeAlpha");
114 builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str());
115 }
116
117 static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
118 const CircleEdgeEffect& circleEffect = drawEffect.castEffect<CircleEdgeEffect>();
119
120 return circleEffect.isStroked() ? 0x1 : 0x0;
121 }
122
123 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {}
124
125 private:
126 typedef GrGLEffect INHERITED;
127 };
128
129
130private:
131 CircleEdgeEffect(bool stroke) : GrEffect() {
132 this->addVertexAttrib(kVec4f_GrSLType);
133 fStroke = stroke;
134 }
135
136 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
137 const CircleEdgeEffect& cee = CastEffect<CircleEdgeEffect>(other);
138 return cee.fStroke == fStroke;
139 }
140
141 bool fStroke;
142
143 GR_DECLARE_EFFECT_TEST;
144
145 typedef GrEffect INHERITED;
146};
147
148GR_DEFINE_EFFECT_TEST(CircleEdgeEffect);
149
150GrEffectRef* CircleEdgeEffect::TestCreate(SkMWCRandom* random,
151 GrContext* context,
152 const GrDrawTargetCaps&,
153 GrTexture* textures[]) {
154 return CircleEdgeEffect::Create(random->nextBool());
155}
156
157///////////////////////////////////////////////////////////////////////////////
158
159/**
160 * The output of this effect is a modulation of the input color and coverage for an axis-aligned
161 * ellipse, specified as center_x, center_y, x_radius, x_radius/y_radius in window space (y-down).
162 */
163
164class EllipseEdgeEffect : public GrEffect {
165public:
166 static GrEffectRef* Create(bool stroke) {
167 // we go through this so we only have one copy of each effect (stroked/filled)
168 static SkAutoTUnref<GrEffectRef> gEllipseStrokeEdgeEffectRef(
169 CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(EllipseEdgeEffect, (true)))));
170 static SkAutoTUnref<GrEffectRef> gEllipseFillEdgeEffectRef(
171 CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(EllipseEdgeEffect, (false)))));
172
173 if (stroke) {
174 gEllipseStrokeEdgeEffectRef.get()->ref();
175 return gEllipseStrokeEdgeEffectRef;
176 } else {
177 gEllipseFillEdgeEffectRef.get()->ref();
178 return gEllipseFillEdgeEffectRef;
179 }
180 }
181
182 virtual void getConstantColorComponents(GrColor* color,
183 uint32_t* validFlags) const SK_OVERRIDE {
184 *validFlags = 0;
185 }
186
187 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
188 return GrTBackendEffectFactory<EllipseEdgeEffect>::getInstance();
189 }
190
191 virtual ~EllipseEdgeEffect() {}
192
193 static const char* Name() { return "EllipseEdge"; }
194
195 inline bool isStroked() const { return fStroke; }
196
197 class GLEffect : public GrGLEffect {
198 public:
199 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
200 : INHERITED (factory) {}
201
202 virtual void emitCode(GrGLShaderBuilder* builder,
203 const GrDrawEffect& drawEffect,
204 EffectKey key,
205 const char* outputColor,
206 const char* inputColor,
207 const TextureSamplerArray& samplers) SK_OVERRIDE {
208 const EllipseEdgeEffect& ellipseEffect = drawEffect.castEffect<EllipseEdgeEffect>();
209
210 const char *vsCenterName, *fsCenterName;
211 const char *vsEdgeName, *fsEdgeName;
212
213 builder->addVarying(kVec2f_GrSLType, "EllipseCenter", &vsCenterName, &fsCenterName);
214 const SkString* attr0Name =
215 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
216 builder->vsCodeAppendf("\t%s = %s;\n", vsCenterName, attr0Name->c_str());
217
218 builder->addVarying(kVec4f_GrSLType, "EllipseEdge", &vsEdgeName, &fsEdgeName);
219 const SkString* attr1Name =
220 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[1]);
221 builder->vsCodeAppendf("\t%s = %s;\n", vsEdgeName, attr1Name->c_str());
222
223 // translate to origin
224 builder->fsCodeAppendf("\tvec2 outerOffset = (%s.xy - %s.xy);\n",
225 builder->fragmentPosition(), fsCenterName);
226 builder->fsCodeAppend("\tvec2 innerOffset = outerOffset;\n");
227 // scale y by xRadius/yRadius
228 builder->fsCodeAppendf("\touterOffset.y *= %s.y;\n", fsEdgeName);
229 builder->fsCodeAppend("\tfloat dOuter = length(outerOffset);\n");
230 // compare outer lengths against xOuterRadius
231 builder->fsCodeAppendf("\tfloat edgeAlpha = clamp(%s.x-dOuter, 0.0, 1.0);\n",
232 fsEdgeName);
233
234 if (ellipseEffect.isStroked()) {
235 builder->fsCodeAppendf("\tinnerOffset.y *= %s.w;\n", fsEdgeName);
236 builder->fsCodeAppend("\tfloat dInner = length(innerOffset);\n");
237
238 // compare inner lengths against xInnerRadius
239 builder->fsCodeAppendf("\tfloat innerAlpha = clamp(dInner-%s.z, 0.0, 1.0);\n",
240 fsEdgeName);
241 builder->fsCodeAppend("\tedgeAlpha *= innerAlpha;\n");
242 }
243
244 SkString modulate;
245 GrGLSLModulate4f(&modulate, inputColor, "edgeAlpha");
246 builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str());
247 }
248
249 static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
250 const EllipseEdgeEffect& ellipseEffect = drawEffect.castEffect<EllipseEdgeEffect>();
251
252 return ellipseEffect.isStroked() ? 0x1 : 0x0;
253 }
254
255 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {
256 }
257
258 private:
259 typedef GrGLEffect INHERITED;
260 };
261
262private:
263 EllipseEdgeEffect(bool stroke) : GrEffect() {
264 this->addVertexAttrib(kVec2f_GrSLType);
265 this->addVertexAttrib(kVec4f_GrSLType);
266 fStroke = stroke;
267 }
268
269 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
270 const EllipseEdgeEffect& eee = CastEffect<EllipseEdgeEffect>(other);
271 return eee.fStroke == fStroke;
272 }
273
274 bool fStroke;
275
276 GR_DECLARE_EFFECT_TEST;
277
278 typedef GrEffect INHERITED;
279};
280
281GR_DEFINE_EFFECT_TEST(EllipseEdgeEffect);
282
283GrEffectRef* EllipseEdgeEffect::TestCreate(SkMWCRandom* random,
284 GrContext* context,
285 const GrDrawTargetCaps&,
286 GrTexture* textures[]) {
287 return EllipseEdgeEffect::Create(random->nextBool());
288}
289
290///////////////////////////////////////////////////////////////////////////////
291
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000292bool GrOvalRenderer::drawOval(GrDrawTarget* target, const GrContext* context, const GrPaint& paint,
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000293 const GrRect& oval, const SkStrokeRec& stroke)
294{
295 if (!paint.isAntiAlias()) {
296 return false;
297 }
298
299 const SkMatrix& vm = context->getMatrix();
300
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000301 // we can draw circles
302 if (SkScalarNearlyEqual(oval.width(), oval.height())
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000303 && circle_stays_circle(vm)) {
304 drawCircle(target, paint, oval, stroke);
305
306 // and axis-aligned ellipses only
307 } else if (vm.rectStaysRect()) {
308 drawEllipse(target, paint, oval, stroke);
309
310 } else {
311 return false;
312 }
313
314 return true;
315}
316
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000317void GrOvalRenderer::drawCircle(GrDrawTarget* target,
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000318 const GrPaint& paint,
319 const GrRect& circle,
320 const SkStrokeRec& stroke)
321{
322 GrDrawState* drawState = target->drawState();
323
324 const SkMatrix& vm = drawState->getViewMatrix();
325 GrPoint center = GrPoint::Make(circle.centerX(), circle.centerY());
326 vm.mapPoints(&center, 1);
327 SkScalar radius = vm.mapRadius(SkScalarHalf(circle.width()));
328 SkScalar strokeWidth = vm.mapRadius(stroke.getWidth());
329
330 GrDrawState::AutoDeviceCoordDraw adcd(drawState);
331 if (!adcd.succeeded()) {
332 return;
333 }
334
335 // position + edge
336 static const GrVertexAttrib kVertexAttribs[] = {
jvanverth@google.com054ae992013-04-01 20:06:51 +0000337 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
338 {kVec4f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding}
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000339 };
340 drawState->setVertexAttribs(kVertexAttribs, SK_ARRAY_COUNT(kVertexAttribs));
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000341 GrAssert(sizeof(CircleVertex) == drawState->getVertexSize());
342
343 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
344 if (!geo.succeeded()) {
345 GrPrintf("Failed to get space for vertices!\n");
346 return;
347 }
348
349 CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices());
350
351 SkStrokeRec::Style style = stroke.getStyle();
352 bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style);
353 enum {
354 // the edge effects share this stage with glyph rendering
355 // (kGlyphMaskStage in GrTextContext) && SW path rendering
356 // (kPathMaskStage in GrSWMaskHelper)
357 kEdgeEffectStage = GrPaint::kTotalStages,
358 };
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000359
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000360 GrEffectRef* effect = CircleEdgeEffect::Create(isStroked);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000361 static const int kCircleEdgeAttrIndex = 1;
362 drawState->setEffect(kEdgeEffectStage, effect, kCircleEdgeAttrIndex)->unref();
363
364 SkScalar innerRadius = 0.0f;
365 SkScalar outerRadius = radius;
366 SkScalar halfWidth = 0;
367 if (style != SkStrokeRec::kFill_Style) {
368 if (SkScalarNearlyZero(strokeWidth)) {
369 halfWidth = SK_ScalarHalf;
370 } else {
371 halfWidth = SkScalarHalf(strokeWidth);
372 }
373
374 outerRadius += halfWidth;
375 if (isStroked) {
376 innerRadius = SkMaxScalar(0, radius - halfWidth);
377 }
378 }
379
bsalomon@google.com58e30fe2013-04-01 19:01:20 +0000380 // The radii are outset for two reasons. First, it allows the shader to simply perform
381 // clamp(distance-to-center - radius, 0, 1). Second, the outer radius is used to compute the
382 // verts of the bounding box that is rendered and the outset ensures the box will cover all
383 // pixels partially covered by the circle.
384 outerRadius += SK_ScalarHalf;
385 innerRadius -= SK_ScalarHalf;
386
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000387 for (int i = 0; i < 4; ++i) {
388 verts[i].fCenter = center;
bsalomon@google.com58e30fe2013-04-01 19:01:20 +0000389 verts[i].fOuterRadius = outerRadius;
390 verts[i].fInnerRadius = innerRadius;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000391 }
392
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000393 SkRect bounds = SkRect::MakeLTRB(
bsalomon@google.com58e30fe2013-04-01 19:01:20 +0000394 center.fX - outerRadius,
395 center.fY - outerRadius,
396 center.fX + outerRadius,
397 center.fY + outerRadius
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000398 );
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000399
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000400 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
401 verts[1].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
402 verts[2].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
403 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000404
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000405 target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4, &bounds);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000406}
407
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000408void GrOvalRenderer::drawEllipse(GrDrawTarget* target,
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000409 const GrPaint& paint,
410 const GrRect& ellipse,
411 const SkStrokeRec& stroke)
412{
413 GrDrawState* drawState = target->drawState();
414#ifdef SK_DEBUG
415 {
416 // we should have checked for this previously
417 bool isAxisAlignedEllipse = drawState->getViewMatrix().rectStaysRect();
418 SkASSERT(paint.isAntiAlias() && isAxisAlignedEllipse);
419 }
420#endif
421
422 const SkMatrix& vm = drawState->getViewMatrix();
423 GrPoint center = GrPoint::Make(ellipse.centerX(), ellipse.centerY());
424 vm.mapPoints(&center, 1);
425 SkRect xformedRect;
426 vm.mapRect(&xformedRect, ellipse);
427
428 GrDrawState::AutoDeviceCoordDraw adcd(drawState);
429 if (!adcd.succeeded()) {
430 return;
431 }
432
433 // position + edge
434 static const GrVertexAttrib kVertexAttribs[] = {
jvanverth@google.com054ae992013-04-01 20:06:51 +0000435 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
436 {kVec2f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding},
437 {kVec4f_GrVertexAttribType, 2*sizeof(GrPoint), kEffect_GrVertexAttribBinding}
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000438 };
439 drawState->setVertexAttribs(kVertexAttribs, SK_ARRAY_COUNT(kVertexAttribs));
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000440 GrAssert(sizeof(EllipseVertex) == drawState->getVertexSize());
441
442 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
443 if (!geo.succeeded()) {
444 GrPrintf("Failed to get space for vertices!\n");
445 return;
446 }
447
448 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices());
449
450 SkStrokeRec::Style style = stroke.getStyle();
451 bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style);
452 enum {
453 // the edge effects share this stage with glyph rendering
454 // (kGlyphMaskStage in GrTextContext) && SW path rendering
455 // (kPathMaskStage in GrSWMaskHelper)
456 kEdgeEffectStage = GrPaint::kTotalStages,
457 };
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000458
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000459 GrEffectRef* effect = EllipseEdgeEffect::Create(isStroked);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000460 static const int kEllipseCenterAttrIndex = 1;
461 static const int kEllipseEdgeAttrIndex = 2;
462 drawState->setEffect(kEdgeEffectStage, effect,
463 kEllipseCenterAttrIndex, kEllipseEdgeAttrIndex)->unref();
464
465 SkScalar xRadius = SkScalarHalf(xformedRect.width());
466 SkScalar yRadius = SkScalarHalf(xformedRect.height());
467 SkScalar innerXRadius = 0.0f;
468 SkScalar innerRatio = 1.0f;
469
470 if (SkStrokeRec::kFill_Style != style) {
471 SkScalar strokeWidth = stroke.getWidth();
472
473 // do (potentially) anisotropic mapping
474 SkVector scaledStroke;
475 scaledStroke.set(strokeWidth, strokeWidth);
476 vm.mapVectors(&scaledStroke, 1);
477
478 if (SkScalarNearlyZero(scaledStroke.length())) {
479 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
480 } else {
481 scaledStroke.scale(0.5f);
482 }
483
484 // this is legit only if scale & translation (which should be the case at the moment)
485 if (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style) {
486 SkScalar innerYRadius = SkMaxScalar(0, yRadius - scaledStroke.fY);
487 if (innerYRadius > SK_ScalarNearlyZero) {
488 innerXRadius = SkMaxScalar(0, xRadius - scaledStroke.fX);
489 innerRatio = innerXRadius/innerYRadius;
490 }
491 }
492 xRadius += scaledStroke.fX;
493 yRadius += scaledStroke.fY;
494 }
495
496 SkScalar outerRatio = SkScalarDiv(xRadius, yRadius);
497
498 for (int i = 0; i < 4; ++i) {
499 verts[i].fCenter = center;
500 verts[i].fOuterXRadius = xRadius + 0.5f;
501 verts[i].fOuterXYRatio = outerRatio;
502 verts[i].fInnerXRadius = innerXRadius - 0.5f;
503 verts[i].fInnerXYRatio = innerRatio;
504 }
505
506 SkScalar L = -xRadius;
507 SkScalar R = +xRadius;
508 SkScalar T = -yRadius;
509 SkScalar B = +yRadius;
510
511 // We've extended the outer x radius out half a pixel to antialias.
512 // Expand the drawn rect here so all the pixels will be captured.
513 L += center.fX - SK_ScalarHalf;
514 R += center.fX + SK_ScalarHalf;
515 T += center.fY - SK_ScalarHalf;
516 B += center.fY + SK_ScalarHalf;
517
518 verts[0].fPos = SkPoint::Make(L, T);
519 verts[1].fPos = SkPoint::Make(R, T);
520 verts[2].fPos = SkPoint::Make(L, B);
521 verts[3].fPos = SkPoint::Make(R, B);
522
523 target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4);
524}