1 /*
2 * Copyright 2016 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 "GrShadowRRectOp.h"
9
10 #include "GrContext.h"
11 #include "GrContextPriv.h"
12 #include "GrDrawOpTest.h"
13 #include "GrMemoryPool.h"
14 #include "GrOpFlushState.h"
15 #include "SkRRectPriv.h"
16 #include "effects/GrShadowGeoProc.h"
17
18 ///////////////////////////////////////////////////////////////////////////////
19 // Circle Data
20 //
21 // We have two possible cases for geometry for a circle:
22
23 // In the case of a normal fill, we draw geometry for the circle as an octagon.
24 static const uint16_t gFillCircleIndices[] = {
25 // enter the octagon
26 // clang-format off
27 0, 1, 8, 1, 2, 8,
28 2, 3, 8, 3, 4, 8,
29 4, 5, 8, 5, 6, 8,
30 6, 7, 8, 7, 0, 8,
31 // clang-format on
32 };
33
34 // For stroked circles, we use two nested octagons.
35 static const uint16_t gStrokeCircleIndices[] = {
36 // enter the octagon
37 // clang-format off
38 0, 1, 9, 0, 9, 8,
39 1, 2, 10, 1, 10, 9,
40 2, 3, 11, 2, 11, 10,
41 3, 4, 12, 3, 12, 11,
42 4, 5, 13, 4, 13, 12,
43 5, 6, 14, 5, 14, 13,
44 6, 7, 15, 6, 15, 14,
45 7, 0, 8, 7, 8, 15,
46 // clang-format on
47 };
48
49 static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
50 static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
51 static const int kVertsPerStrokeCircle = 16;
52 static const int kVertsPerFillCircle = 9;
53
circle_type_to_vert_count(bool stroked)54 static int circle_type_to_vert_count(bool stroked) {
55 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
56 }
57
circle_type_to_index_count(bool stroked)58 static int circle_type_to_index_count(bool stroked) {
59 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
60 }
61
circle_type_to_indices(bool stroked)62 static const uint16_t* circle_type_to_indices(bool stroked) {
63 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
64 }
65
66 ///////////////////////////////////////////////////////////////////////////////
67 // RoundRect Data
68 //
69 // The geometry for a shadow roundrect is similar to a 9-patch:
70 // ____________
71 // |_|________|_|
72 // | | | |
73 // | | | |
74 // | | | |
75 // |_|________|_|
76 // |_|________|_|
77 //
78 // However, each corner is rendered as a fan rather than a simple quad, as below. (The diagram
79 // shows the upper part of the upper left corner. The bottom triangle would similarly be split
80 // into two triangles.)
81 // ________
82 // |\ \ |
83 // | \ \ |
84 // | \\ |
85 // | \|
86 // --------
87 //
88 // The center of the fan handles the curve of the corner. For roundrects where the stroke width
89 // is greater than the corner radius, the outer triangles blend from the curve to the straight
90 // sides. Otherwise these triangles will be degenerate.
91 //
92 // In the case where the stroke width is greater than the corner radius and the
93 // blur radius (overstroke), we add additional geometry to mark out the rectangle in the center.
94 // This rectangle extends the coverage values of the center edges of the 9-patch.
95 // ____________
96 // |_|________|_|
97 // | |\ ____ /| |
98 // | | | | | |
99 // | | |____| | |
100 // |_|/______\|_|
101 // |_|________|_|
102 //
103 // For filled rrects we reuse the stroke geometry but add an additional quad to the center.
104
105 static const uint16_t gRRectIndices[] = {
106 // clang-format off
107 // overstroke quads
108 // we place this at the beginning so that we can skip these indices when rendering as filled
109 0, 6, 25, 0, 25, 24,
110 6, 18, 27, 6, 27, 25,
111 18, 12, 26, 18, 26, 27,
112 12, 0, 24, 12, 24, 26,
113
114 // corners
115 0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5,
116 6, 11, 10, 6, 10, 9, 6, 9, 8, 6, 8, 7,
117 12, 17, 16, 12, 16, 15, 12, 15, 14, 12, 14, 13,
118 18, 19, 20, 18, 20, 21, 18, 21, 22, 18, 22, 23,
119
120 // edges
121 0, 5, 11, 0, 11, 6,
122 6, 7, 19, 6, 19, 18,
123 18, 23, 17, 18, 17, 12,
124 12, 13, 1, 12, 1, 0,
125
126 // fill quad
127 // we place this at the end so that we can skip these indices when rendering as stroked
128 0, 6, 18, 0, 18, 12,
129 // clang-format on
130 };
131
132 // overstroke count
133 static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
134 // simple stroke count skips overstroke indices
135 static const int kIndicesPerStrokeRRect = kIndicesPerOverstrokeRRect - 6*4;
136 // fill count adds final quad to stroke count
137 static const int kIndicesPerFillRRect = kIndicesPerStrokeRRect + 6;
138 static const int kVertsPerStrokeRRect = 24;
139 static const int kVertsPerOverstrokeRRect = 28;
140 static const int kVertsPerFillRRect = 24;
141
142 enum RRectType {
143 kFill_RRectType,
144 kStroke_RRectType,
145 kOverstroke_RRectType,
146 };
147
rrect_type_to_vert_count(RRectType type)148 static int rrect_type_to_vert_count(RRectType type) {
149 switch (type) {
150 case kFill_RRectType:
151 return kVertsPerFillRRect;
152 case kStroke_RRectType:
153 return kVertsPerStrokeRRect;
154 case kOverstroke_RRectType:
155 return kVertsPerOverstrokeRRect;
156 }
157 SK_ABORT("Invalid type");
158 return 0;
159 }
160
rrect_type_to_index_count(RRectType type)161 static int rrect_type_to_index_count(RRectType type) {
162 switch (type) {
163 case kFill_RRectType:
164 return kIndicesPerFillRRect;
165 case kStroke_RRectType:
166 return kIndicesPerStrokeRRect;
167 case kOverstroke_RRectType:
168 return kIndicesPerOverstrokeRRect;
169 }
170 SK_ABORT("Invalid type");
171 return 0;
172 }
173
rrect_type_to_indices(RRectType type)174 static const uint16_t* rrect_type_to_indices(RRectType type) {
175 switch (type) {
176 case kFill_RRectType:
177 case kStroke_RRectType:
178 return gRRectIndices + 6*4;
179 case kOverstroke_RRectType:
180 return gRRectIndices;
181 }
182 SK_ABORT("Invalid type");
183 return nullptr;
184 }
185
186 ///////////////////////////////////////////////////////////////////////////////
187 namespace {
188
189 class ShadowCircularRRectOp final : public GrMeshDrawOp {
190 public:
191 DEFINE_OP_CLASS_ID
192
193 // An insetWidth > 1/2 rect width or height indicates a simple fill.
ShadowCircularRRectOp(GrColor color,const SkRect & devRect,float devRadius,bool isCircle,float blurRadius,float insetWidth)194 ShadowCircularRRectOp(GrColor color, const SkRect& devRect,
195 float devRadius, bool isCircle, float blurRadius, float insetWidth)
196 : INHERITED(ClassID()) {
197 SkRect bounds = devRect;
198 SkASSERT(insetWidth > 0);
199 SkScalar innerRadius = 0.0f;
200 SkScalar outerRadius = devRadius;
201 SkScalar umbraInset;
202
203 RRectType type = kFill_RRectType;
204 if (isCircle) {
205 umbraInset = 0;
206 } else {
207 umbraInset = SkTMax(outerRadius, blurRadius);
208 }
209
210 // If stroke is greater than width or height, this is still a fill,
211 // otherwise we compute stroke params.
212 if (isCircle) {
213 innerRadius = devRadius - insetWidth;
214 type = innerRadius > 0 ? kStroke_RRectType : kFill_RRectType;
215 } else {
216 if (insetWidth <= 0.5f*SkTMin(devRect.width(), devRect.height())) {
217 // We don't worry about a real inner radius, we just need to know if we
218 // need to create overstroke vertices.
219 innerRadius = SkTMax(insetWidth - umbraInset, 0.0f);
220 type = innerRadius > 0 ? kOverstroke_RRectType : kStroke_RRectType;
221 }
222 }
223
224 this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
225
226 fGeoData.emplace_back(Geometry{color, outerRadius, umbraInset, innerRadius,
227 blurRadius, bounds, type, isCircle});
228 if (isCircle) {
229 fVertCount = circle_type_to_vert_count(kStroke_RRectType == type);
230 fIndexCount = circle_type_to_index_count(kStroke_RRectType == type);
231 } else {
232 fVertCount = rrect_type_to_vert_count(type);
233 fIndexCount = rrect_type_to_index_count(type);
234 }
235 }
236
name() const237 const char* name() const override { return "ShadowCircularRRectOp"; }
238
239 #ifdef SK_DEBUG
dumpInfo() const240 SkString dumpInfo() const override {
241 SkString string;
242 for (int i = 0; i < fGeoData.count(); ++i) {
243 string.appendf(
244 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
245 "OuterRad: %.2f, Umbra: %.2f, InnerRad: %.2f, BlurRad: %.2f\n",
246 fGeoData[i].fColor, fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
247 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
248 fGeoData[i].fOuterRadius, fGeoData[i].fUmbraInset,
249 fGeoData[i].fInnerRadius, fGeoData[i].fBlurRadius);
250 }
251 string.append(INHERITED::dumpInfo());
252 return string;
253 }
254 #endif
255
fixedFunctionFlags() const256 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
257
finalize(const GrCaps &,const GrAppliedClip *)258 GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*) override {
259 return GrProcessorSet::EmptySetAnalysis();
260 }
261
262 private:
263 struct Geometry {
264 GrColor fColor;
265 SkScalar fOuterRadius;
266 SkScalar fUmbraInset;
267 SkScalar fInnerRadius;
268 SkScalar fBlurRadius;
269 SkRect fDevBounds;
270 RRectType fType;
271 bool fIsCircle;
272 };
273
274 struct CircleVertex {
275 SkPoint fPos;
276 GrColor fColor;
277 SkPoint fOffset;
278 SkScalar fDistanceCorrection;
279 };
280
fillInCircleVerts(const Geometry & args,bool isStroked,CircleVertex ** verts) const281 void fillInCircleVerts(const Geometry& args, bool isStroked, CircleVertex** verts) const {
282
283 GrColor color = args.fColor;
284 SkScalar outerRadius = args.fOuterRadius;
285 SkScalar innerRadius = args.fInnerRadius;
286 SkScalar blurRadius = args.fBlurRadius;
287 SkScalar distanceCorrection = outerRadius / blurRadius;
288
289 const SkRect& bounds = args.fDevBounds;
290
291 // The inner radius in the vertex data must be specified in normalized space.
292 innerRadius = innerRadius / outerRadius;
293
294 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
295 SkScalar halfWidth = 0.5f * bounds.width();
296 SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1
297
298 (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth);
299 (*verts)->fColor = color;
300 (*verts)->fOffset = SkPoint::Make(-octOffset, -1);
301 (*verts)->fDistanceCorrection = distanceCorrection;
302 (*verts)++;
303
304 (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth);
305 (*verts)->fColor = color;
306 (*verts)->fOffset = SkPoint::Make(octOffset, -1);
307 (*verts)->fDistanceCorrection = distanceCorrection;
308 (*verts)++;
309
310 (*verts)->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth);
311 (*verts)->fColor = color;
312 (*verts)->fOffset = SkPoint::Make(1, -octOffset);
313 (*verts)->fDistanceCorrection = distanceCorrection;
314 (*verts)++;
315
316 (*verts)->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth);
317 (*verts)->fColor = color;
318 (*verts)->fOffset = SkPoint::Make(1, octOffset);
319 (*verts)->fDistanceCorrection = distanceCorrection;
320 (*verts)++;
321
322 (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth);
323 (*verts)->fColor = color;
324 (*verts)->fOffset = SkPoint::Make(octOffset, 1);
325 (*verts)->fDistanceCorrection = distanceCorrection;
326 (*verts)++;
327
328 (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth);
329 (*verts)->fColor = color;
330 (*verts)->fOffset = SkPoint::Make(-octOffset, 1);
331 (*verts)->fDistanceCorrection = distanceCorrection;
332 (*verts)++;
333
334 (*verts)->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth);
335 (*verts)->fColor = color;
336 (*verts)->fOffset = SkPoint::Make(-1, octOffset);
337 (*verts)->fDistanceCorrection = distanceCorrection;
338 (*verts)++;
339
340 (*verts)->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth);
341 (*verts)->fColor = color;
342 (*verts)->fOffset = SkPoint::Make(-1, -octOffset);
343 (*verts)->fDistanceCorrection = distanceCorrection;
344 (*verts)++;
345
346 if (isStroked) {
347 // compute the inner ring
348
349 // cosine and sine of pi/8
350 SkScalar c = 0.923579533f;
351 SkScalar s = 0.382683432f;
352 SkScalar r = args.fInnerRadius;
353
354 (*verts)->fPos = center + SkPoint::Make(-s * r, -c * r);
355 (*verts)->fColor = color;
356 (*verts)->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius);
357 (*verts)->fDistanceCorrection = distanceCorrection;
358 (*verts)++;
359
360 (*verts)->fPos = center + SkPoint::Make(s * r, -c * r);
361 (*verts)->fColor = color;
362 (*verts)->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius);
363 (*verts)->fDistanceCorrection = distanceCorrection;
364 (*verts)++;
365
366 (*verts)->fPos = center + SkPoint::Make(c * r, -s * r);
367 (*verts)->fColor = color;
368 (*verts)->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius);
369 (*verts)->fDistanceCorrection = distanceCorrection;
370 (*verts)++;
371
372 (*verts)->fPos = center + SkPoint::Make(c * r, s * r);
373 (*verts)->fColor = color;
374 (*verts)->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius);
375 (*verts)->fDistanceCorrection = distanceCorrection;
376 (*verts)++;
377
378 (*verts)->fPos = center + SkPoint::Make(s * r, c * r);
379 (*verts)->fColor = color;
380 (*verts)->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius);
381 (*verts)->fDistanceCorrection = distanceCorrection;
382 (*verts)++;
383
384 (*verts)->fPos = center + SkPoint::Make(-s * r, c * r);
385 (*verts)->fColor = color;
386 (*verts)->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius);
387 (*verts)->fDistanceCorrection = distanceCorrection;
388 (*verts)++;
389
390 (*verts)->fPos = center + SkPoint::Make(-c * r, s * r);
391 (*verts)->fColor = color;
392 (*verts)->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius);
393 (*verts)->fDistanceCorrection = distanceCorrection;
394 (*verts)++;
395
396 (*verts)->fPos = center + SkPoint::Make(-c * r, -s * r);
397 (*verts)->fColor = color;
398 (*verts)->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius);
399 (*verts)->fDistanceCorrection = distanceCorrection;
400 (*verts)++;
401 } else {
402 // filled
403 (*verts)->fPos = center;
404 (*verts)->fColor = color;
405 (*verts)->fOffset = SkPoint::Make(0, 0);
406 (*verts)->fDistanceCorrection = distanceCorrection;
407 (*verts)++;
408 }
409 }
410
fillInRRectVerts(const Geometry & args,CircleVertex ** verts) const411 void fillInRRectVerts(const Geometry& args, CircleVertex** verts) const {
412 GrColor color = args.fColor;
413 SkScalar outerRadius = args.fOuterRadius;
414
415 const SkRect& bounds = args.fDevBounds;
416
417 SkScalar umbraInset = args.fUmbraInset;
418 SkScalar minDim = 0.5f*SkTMin(bounds.width(), bounds.height());
419 if (umbraInset > minDim) {
420 umbraInset = minDim;
421 }
422
423 SkScalar xInner[4] = { bounds.fLeft + umbraInset, bounds.fRight - umbraInset,
424 bounds.fLeft + umbraInset, bounds.fRight - umbraInset };
425 SkScalar xMid[4] = { bounds.fLeft + outerRadius, bounds.fRight - outerRadius,
426 bounds.fLeft + outerRadius, bounds.fRight - outerRadius };
427 SkScalar xOuter[4] = { bounds.fLeft, bounds.fRight,
428 bounds.fLeft, bounds.fRight };
429 SkScalar yInner[4] = { bounds.fTop + umbraInset, bounds.fTop + umbraInset,
430 bounds.fBottom - umbraInset, bounds.fBottom - umbraInset };
431 SkScalar yMid[4] = { bounds.fTop + outerRadius, bounds.fTop + outerRadius,
432 bounds.fBottom - outerRadius, bounds.fBottom - outerRadius };
433 SkScalar yOuter[4] = { bounds.fTop, bounds.fTop,
434 bounds.fBottom, bounds.fBottom };
435
436 SkScalar blurRadius = args.fBlurRadius;
437
438 // In the case where we have to inset more for the umbra, our two triangles in the
439 // corner get skewed to a diamond rather than a square. To correct for that,
440 // we also skew the vectors we send to the shader that help define the circle.
441 // By doing so, we end up with a quarter circle in the corner rather than the
442 // elliptical curve.
443
444 // This is a bit magical, but it gives us the correct results at extrema:
445 // a) umbraInset == outerRadius produces an orthogonal vector
446 // b) outerRadius == 0 produces a diagonal vector
447 // And visually the corner looks correct.
448 SkVector outerVec = SkVector::Make(outerRadius - umbraInset, -outerRadius - umbraInset);
449 outerVec.normalize();
450 // We want the circle edge to fall fractionally along the diagonal at
451 // (sqrt(2)*(umbraInset - outerRadius) + outerRadius)/sqrt(2)*umbraInset
452 //
453 // Setting the components of the diagonal offset to the following value will give us that.
454 SkScalar diagVal = umbraInset / (SK_ScalarSqrt2*(outerRadius - umbraInset) - outerRadius);
455 SkVector diagVec = SkVector::Make(diagVal, diagVal);
456 SkScalar distanceCorrection = umbraInset / blurRadius;
457
458 // build corner by corner
459 for (int i = 0; i < 4; ++i) {
460 // inner point
461 (*verts)->fPos = SkPoint::Make(xInner[i], yInner[i]);
462 (*verts)->fColor = color;
463 (*verts)->fOffset = SkVector::Make(0, 0);
464 (*verts)->fDistanceCorrection = distanceCorrection;
465 (*verts)++;
466
467 // outer points
468 (*verts)->fPos = SkPoint::Make(xOuter[i], yInner[i]);
469 (*verts)->fColor = color;
470 (*verts)->fOffset = SkVector::Make(0, -1);
471 (*verts)->fDistanceCorrection = distanceCorrection;
472 (*verts)++;
473
474 (*verts)->fPos = SkPoint::Make(xOuter[i], yMid[i]);
475 (*verts)->fColor = color;
476 (*verts)->fOffset = outerVec;
477 (*verts)->fDistanceCorrection = distanceCorrection;
478 (*verts)++;
479
480 (*verts)->fPos = SkPoint::Make(xOuter[i], yOuter[i]);
481 (*verts)->fColor = color;
482 (*verts)->fOffset = diagVec;
483 (*verts)->fDistanceCorrection = distanceCorrection;
484 (*verts)++;
485
486 (*verts)->fPos = SkPoint::Make(xMid[i], yOuter[i]);
487 (*verts)->fColor = color;
488 (*verts)->fOffset = outerVec;
489 (*verts)->fDistanceCorrection = distanceCorrection;
490 (*verts)++;
491
492 (*verts)->fPos = SkPoint::Make(xInner[i], yOuter[i]);
493 (*verts)->fColor = color;
494 (*verts)->fOffset = SkVector::Make(0, -1);
495 (*verts)->fDistanceCorrection = distanceCorrection;
496 (*verts)++;
497 }
498
499 // Add the additional vertices for overstroked rrects.
500 // Effectively this is an additional stroked rrect, with its
501 // parameters equal to those in the center of the 9-patch. This will
502 // give constant values across this inner ring.
503 if (kOverstroke_RRectType == args.fType) {
504 SkASSERT(args.fInnerRadius > 0.0f);
505
506 SkScalar inset = umbraInset + args.fInnerRadius;
507
508 // TL
509 (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fTop + inset);
510 (*verts)->fColor = color;
511 (*verts)->fOffset = SkPoint::Make(0, 0);
512 (*verts)->fDistanceCorrection = distanceCorrection;
513 (*verts)++;
514
515 // TR
516 (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fTop + inset);
517 (*verts)->fColor = color;
518 (*verts)->fOffset = SkPoint::Make(0, 0);
519 (*verts)->fDistanceCorrection = distanceCorrection;
520 (*verts)++;
521
522 // BL
523 (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fBottom - inset);
524 (*verts)->fColor = color;
525 (*verts)->fOffset = SkPoint::Make(0, 0);
526 (*verts)->fDistanceCorrection = distanceCorrection;
527 (*verts)++;
528
529 // BR
530 (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fBottom - inset);
531 (*verts)->fColor = color;
532 (*verts)->fOffset = SkPoint::Make(0, 0);
533 (*verts)->fDistanceCorrection = distanceCorrection;
534 (*verts)++;
535 }
536
537 }
538
onPrepareDraws(Target * target)539 void onPrepareDraws(Target* target) override {
540 // Setup geometry processor
541 sk_sp<GrGeometryProcessor> gp = GrRRectShadowGeoProc::Make();
542
543 int instanceCount = fGeoData.count();
544 SkASSERT(sizeof(CircleVertex) == gp->vertexStride());
545
546 sk_sp<const GrBuffer> vertexBuffer;
547 int firstVertex;
548 CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(
549 sizeof(CircleVertex), fVertCount, &vertexBuffer, &firstVertex);
550 if (!verts) {
551 SkDebugf("Could not allocate vertices\n");
552 return;
553 }
554
555 sk_sp<const GrBuffer> indexBuffer;
556 int firstIndex = 0;
557 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
558 if (!indices) {
559 SkDebugf("Could not allocate indices\n");
560 return;
561 }
562
563 int currStartVertex = 0;
564 for (int i = 0; i < instanceCount; i++) {
565 const Geometry& args = fGeoData[i];
566
567 if (args.fIsCircle) {
568 bool isStroked = SkToBool(kStroke_RRectType == args.fType);
569 this->fillInCircleVerts(args, isStroked, &verts);
570
571 const uint16_t* primIndices = circle_type_to_indices(isStroked);
572 const int primIndexCount = circle_type_to_index_count(isStroked);
573 for (int i = 0; i < primIndexCount; ++i) {
574 *indices++ = primIndices[i] + currStartVertex;
575 }
576
577 currStartVertex += circle_type_to_vert_count(isStroked);
578
579 } else {
580 this->fillInRRectVerts(args, &verts);
581
582 const uint16_t* primIndices = rrect_type_to_indices(args.fType);
583 const int primIndexCount = rrect_type_to_index_count(args.fType);
584 for (int i = 0; i < primIndexCount; ++i) {
585 *indices++ = primIndices[i] + currStartVertex;
586 }
587
588 currStartVertex += rrect_type_to_vert_count(args.fType);
589 }
590 }
591
592 static const uint32_t kPipelineFlags = 0;
593 auto pipe = target->makePipeline(kPipelineFlags, GrProcessorSet::MakeEmptySet(),
594 target->detachAppliedClip());
595
596 GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
597 mesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
598 GrPrimitiveRestart::kNo);
599 mesh->setVertexData(std::move(vertexBuffer), firstVertex);
600 target->draw(std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
601 }
602
onCombineIfPossible(GrOp * t,const GrCaps & caps)603 CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
604 ShadowCircularRRectOp* that = t->cast<ShadowCircularRRectOp>();
605 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
606 fVertCount += that->fVertCount;
607 fIndexCount += that->fIndexCount;
608 return CombineResult::kMerged;
609 }
610
611 SkSTArray<1, Geometry, true> fGeoData;
612 int fVertCount;
613 int fIndexCount;
614
615 typedef GrMeshDrawOp INHERITED;
616 };
617
618 } // anonymous namespace
619
620 ///////////////////////////////////////////////////////////////////////////////
621
622 namespace GrShadowRRectOp {
Make(GrContext * context,GrColor color,const SkMatrix & viewMatrix,const SkRRect & rrect,SkScalar blurWidth,SkScalar insetWidth)623 std::unique_ptr<GrDrawOp> Make(GrContext* context,
624 GrColor color,
625 const SkMatrix& viewMatrix,
626 const SkRRect& rrect,
627 SkScalar blurWidth,
628 SkScalar insetWidth) {
629 // Shadow rrect ops only handle simple circular rrects.
630 SkASSERT(viewMatrix.isSimilarity() && SkRRectPriv::EqualRadii(rrect));
631
632 // Do any matrix crunching before we reset the draw state for device coords.
633 const SkRect& rrectBounds = rrect.getBounds();
634 SkRect bounds;
635 viewMatrix.mapRect(&bounds, rrectBounds);
636
637 // Map radius and inset. As the matrix is a similarity matrix, this should be isotropic.
638 SkScalar radius = SkRRectPriv::GetSimpleRadii(rrect).fX;
639 SkScalar matrixFactor = viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewX];
640 SkScalar scaledRadius = SkScalarAbs(radius*matrixFactor);
641 SkScalar scaledInsetWidth = SkScalarAbs(insetWidth*matrixFactor);
642
643 GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
644
645 return pool->allocate<ShadowCircularRRectOp>(color, bounds,
646 scaledRadius,
647 rrect.isOval(),
648 blurWidth,
649 scaledInsetWidth);
650 }
651 }
652
653 ///////////////////////////////////////////////////////////////////////////////
654
655 #if GR_TEST_UTILS
656
GR_DRAW_OP_TEST_DEFINE(ShadowRRectOp)657 GR_DRAW_OP_TEST_DEFINE(ShadowRRectOp) {
658 // create a similarity matrix
659 SkScalar rotate = random->nextSScalar1() * 360.f;
660 SkScalar translateX = random->nextSScalar1() * 1000.f;
661 SkScalar translateY = random->nextSScalar1() * 1000.f;
662 SkScalar scale;
663 do {
664 scale = random->nextSScalar1() * 100.f;
665 } while (scale == 0);
666 SkMatrix viewMatrix;
667 viewMatrix.setRotate(rotate);
668 viewMatrix.postTranslate(translateX, translateY);
669 viewMatrix.postScale(scale, scale);
670 SkScalar insetWidth = random->nextSScalar1() * 72.f;
671 SkScalar blurWidth = random->nextSScalar1() * 72.f;
672 bool isCircle = random->nextBool();
673 // This op doesn't use a full GrPaint, just a color.
674 GrColor color = paint.getColor4f().toBytes_RGBA();
675 if (isCircle) {
676 SkRect circle = GrTest::TestSquare(random);
677 SkRRect rrect = SkRRect::MakeOval(circle);
678 return GrShadowRRectOp::Make(context, color, viewMatrix, rrect, blurWidth, insetWidth);
679 } else {
680 SkRRect rrect;
681 do {
682 // This may return a rrect with elliptical corners, which we don't support.
683 rrect = GrTest::TestRRectSimple(random);
684 } while (!SkRRectPriv::IsSimpleCircular(rrect));
685 return GrShadowRRectOp::Make(context, color, viewMatrix, rrect, blurWidth, insetWidth);
686 }
687 }
688
689 #endif
690