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 "src/gpu/ops/GrShadowRRectOp.h"
9
10 #include "include/private/GrRecordingContext.h"
11 #include "src/core/SkRRectPriv.h"
12 #include "src/gpu/GrDrawOpTest.h"
13 #include "src/gpu/GrMemoryPool.h"
14 #include "src/gpu/GrOpFlushState.h"
15 #include "src/gpu/GrRecordingContextPriv.h"
16 #include "src/gpu/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 }
159
rrect_type_to_index_count(RRectType type)160 static int rrect_type_to_index_count(RRectType type) {
161 switch (type) {
162 case kFill_RRectType:
163 return kIndicesPerFillRRect;
164 case kStroke_RRectType:
165 return kIndicesPerStrokeRRect;
166 case kOverstroke_RRectType:
167 return kIndicesPerOverstrokeRRect;
168 }
169 SK_ABORT("Invalid type");
170 }
171
rrect_type_to_indices(RRectType type)172 static const uint16_t* rrect_type_to_indices(RRectType type) {
173 switch (type) {
174 case kFill_RRectType:
175 case kStroke_RRectType:
176 return gRRectIndices + 6*4;
177 case kOverstroke_RRectType:
178 return gRRectIndices;
179 }
180 SK_ABORT("Invalid type");
181 }
182
183 ///////////////////////////////////////////////////////////////////////////////
184 namespace {
185
186 class ShadowCircularRRectOp final : public GrMeshDrawOp {
187 public:
188 DEFINE_OP_CLASS_ID
189
190 // 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)191 ShadowCircularRRectOp(GrColor color, const SkRect& devRect,
192 float devRadius, bool isCircle, float blurRadius, float insetWidth)
193 : INHERITED(ClassID()) {
194 SkRect bounds = devRect;
195 SkASSERT(insetWidth > 0);
196 SkScalar innerRadius = 0.0f;
197 SkScalar outerRadius = devRadius;
198 SkScalar umbraInset;
199
200 RRectType type = kFill_RRectType;
201 if (isCircle) {
202 umbraInset = 0;
203 } else {
204 umbraInset = SkTMax(outerRadius, blurRadius);
205 }
206
207 // If stroke is greater than width or height, this is still a fill,
208 // otherwise we compute stroke params.
209 if (isCircle) {
210 innerRadius = devRadius - insetWidth;
211 type = innerRadius > 0 ? kStroke_RRectType : kFill_RRectType;
212 } else {
213 if (insetWidth <= 0.5f*SkTMin(devRect.width(), devRect.height())) {
214 // We don't worry about a real inner radius, we just need to know if we
215 // need to create overstroke vertices.
216 innerRadius = SkTMax(insetWidth - umbraInset, 0.0f);
217 type = innerRadius > 0 ? kOverstroke_RRectType : kStroke_RRectType;
218 }
219 }
220
221 this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
222
223 fGeoData.emplace_back(Geometry{color, outerRadius, umbraInset, innerRadius,
224 blurRadius, bounds, type, isCircle});
225 if (isCircle) {
226 fVertCount = circle_type_to_vert_count(kStroke_RRectType == type);
227 fIndexCount = circle_type_to_index_count(kStroke_RRectType == type);
228 } else {
229 fVertCount = rrect_type_to_vert_count(type);
230 fIndexCount = rrect_type_to_index_count(type);
231 }
232 }
233
name() const234 const char* name() const override { return "ShadowCircularRRectOp"; }
235
236 #ifdef SK_DEBUG
dumpInfo() const237 SkString dumpInfo() const override {
238 SkString string;
239 for (int i = 0; i < fGeoData.count(); ++i) {
240 string.appendf(
241 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
242 "OuterRad: %.2f, Umbra: %.2f, InnerRad: %.2f, BlurRad: %.2f\n",
243 fGeoData[i].fColor, fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
244 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
245 fGeoData[i].fOuterRadius, fGeoData[i].fUmbraInset,
246 fGeoData[i].fInnerRadius, fGeoData[i].fBlurRadius);
247 }
248 string.append(INHERITED::dumpInfo());
249 return string;
250 }
251 #endif
252
fixedFunctionFlags() const253 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
254
finalize(const GrCaps &,const GrAppliedClip *,bool hasMixedSampledCoverage,GrClampType)255 GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*,
256 bool hasMixedSampledCoverage, GrClampType) override {
257 return GrProcessorSet::EmptySetAnalysis();
258 }
259
260 private:
261 struct Geometry {
262 GrColor fColor;
263 SkScalar fOuterRadius;
264 SkScalar fUmbraInset;
265 SkScalar fInnerRadius;
266 SkScalar fBlurRadius;
267 SkRect fDevBounds;
268 RRectType fType;
269 bool fIsCircle;
270 };
271
272 struct CircleVertex {
273 SkPoint fPos;
274 GrColor fColor;
275 SkPoint fOffset;
276 SkScalar fDistanceCorrection;
277 };
278
fillInCircleVerts(const Geometry & args,bool isStroked,CircleVertex ** verts) const279 void fillInCircleVerts(const Geometry& args, bool isStroked, CircleVertex** verts) const {
280
281 GrColor color = args.fColor;
282 SkScalar outerRadius = args.fOuterRadius;
283 SkScalar innerRadius = args.fInnerRadius;
284 SkScalar blurRadius = args.fBlurRadius;
285 SkScalar distanceCorrection = outerRadius / blurRadius;
286
287 const SkRect& bounds = args.fDevBounds;
288
289 // The inner radius in the vertex data must be specified in normalized space.
290 innerRadius = innerRadius / outerRadius;
291
292 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
293 SkScalar halfWidth = 0.5f * bounds.width();
294 SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1
295
296 (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth);
297 (*verts)->fColor = color;
298 (*verts)->fOffset = SkPoint::Make(-octOffset, -1);
299 (*verts)->fDistanceCorrection = distanceCorrection;
300 (*verts)++;
301
302 (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth);
303 (*verts)->fColor = color;
304 (*verts)->fOffset = SkPoint::Make(octOffset, -1);
305 (*verts)->fDistanceCorrection = distanceCorrection;
306 (*verts)++;
307
308 (*verts)->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth);
309 (*verts)->fColor = color;
310 (*verts)->fOffset = SkPoint::Make(1, -octOffset);
311 (*verts)->fDistanceCorrection = distanceCorrection;
312 (*verts)++;
313
314 (*verts)->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth);
315 (*verts)->fColor = color;
316 (*verts)->fOffset = SkPoint::Make(1, octOffset);
317 (*verts)->fDistanceCorrection = distanceCorrection;
318 (*verts)++;
319
320 (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth);
321 (*verts)->fColor = color;
322 (*verts)->fOffset = SkPoint::Make(octOffset, 1);
323 (*verts)->fDistanceCorrection = distanceCorrection;
324 (*verts)++;
325
326 (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth);
327 (*verts)->fColor = color;
328 (*verts)->fOffset = SkPoint::Make(-octOffset, 1);
329 (*verts)->fDistanceCorrection = distanceCorrection;
330 (*verts)++;
331
332 (*verts)->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth);
333 (*verts)->fColor = color;
334 (*verts)->fOffset = SkPoint::Make(-1, octOffset);
335 (*verts)->fDistanceCorrection = distanceCorrection;
336 (*verts)++;
337
338 (*verts)->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth);
339 (*verts)->fColor = color;
340 (*verts)->fOffset = SkPoint::Make(-1, -octOffset);
341 (*verts)->fDistanceCorrection = distanceCorrection;
342 (*verts)++;
343
344 if (isStroked) {
345 // compute the inner ring
346
347 // cosine and sine of pi/8
348 SkScalar c = 0.923579533f;
349 SkScalar s = 0.382683432f;
350 SkScalar r = args.fInnerRadius;
351
352 (*verts)->fPos = center + SkPoint::Make(-s * r, -c * r);
353 (*verts)->fColor = color;
354 (*verts)->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius);
355 (*verts)->fDistanceCorrection = distanceCorrection;
356 (*verts)++;
357
358 (*verts)->fPos = center + SkPoint::Make(s * r, -c * r);
359 (*verts)->fColor = color;
360 (*verts)->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius);
361 (*verts)->fDistanceCorrection = distanceCorrection;
362 (*verts)++;
363
364 (*verts)->fPos = center + SkPoint::Make(c * r, -s * r);
365 (*verts)->fColor = color;
366 (*verts)->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius);
367 (*verts)->fDistanceCorrection = distanceCorrection;
368 (*verts)++;
369
370 (*verts)->fPos = center + SkPoint::Make(c * r, s * r);
371 (*verts)->fColor = color;
372 (*verts)->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius);
373 (*verts)->fDistanceCorrection = distanceCorrection;
374 (*verts)++;
375
376 (*verts)->fPos = center + SkPoint::Make(s * r, c * r);
377 (*verts)->fColor = color;
378 (*verts)->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius);
379 (*verts)->fDistanceCorrection = distanceCorrection;
380 (*verts)++;
381
382 (*verts)->fPos = center + SkPoint::Make(-s * r, c * r);
383 (*verts)->fColor = color;
384 (*verts)->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius);
385 (*verts)->fDistanceCorrection = distanceCorrection;
386 (*verts)++;
387
388 (*verts)->fPos = center + SkPoint::Make(-c * r, s * r);
389 (*verts)->fColor = color;
390 (*verts)->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius);
391 (*verts)->fDistanceCorrection = distanceCorrection;
392 (*verts)++;
393
394 (*verts)->fPos = center + SkPoint::Make(-c * r, -s * r);
395 (*verts)->fColor = color;
396 (*verts)->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius);
397 (*verts)->fDistanceCorrection = distanceCorrection;
398 (*verts)++;
399 } else {
400 // filled
401 (*verts)->fPos = center;
402 (*verts)->fColor = color;
403 (*verts)->fOffset = SkPoint::Make(0, 0);
404 (*verts)->fDistanceCorrection = distanceCorrection;
405 (*verts)++;
406 }
407 }
408
fillInRRectVerts(const Geometry & args,CircleVertex ** verts) const409 void fillInRRectVerts(const Geometry& args, CircleVertex** verts) const {
410 GrColor color = args.fColor;
411 SkScalar outerRadius = args.fOuterRadius;
412
413 const SkRect& bounds = args.fDevBounds;
414
415 SkScalar umbraInset = args.fUmbraInset;
416 SkScalar minDim = 0.5f*SkTMin(bounds.width(), bounds.height());
417 if (umbraInset > minDim) {
418 umbraInset = minDim;
419 }
420
421 SkScalar xInner[4] = { bounds.fLeft + umbraInset, bounds.fRight - umbraInset,
422 bounds.fLeft + umbraInset, bounds.fRight - umbraInset };
423 SkScalar xMid[4] = { bounds.fLeft + outerRadius, bounds.fRight - outerRadius,
424 bounds.fLeft + outerRadius, bounds.fRight - outerRadius };
425 SkScalar xOuter[4] = { bounds.fLeft, bounds.fRight,
426 bounds.fLeft, bounds.fRight };
427 SkScalar yInner[4] = { bounds.fTop + umbraInset, bounds.fTop + umbraInset,
428 bounds.fBottom - umbraInset, bounds.fBottom - umbraInset };
429 SkScalar yMid[4] = { bounds.fTop + outerRadius, bounds.fTop + outerRadius,
430 bounds.fBottom - outerRadius, bounds.fBottom - outerRadius };
431 SkScalar yOuter[4] = { bounds.fTop, bounds.fTop,
432 bounds.fBottom, bounds.fBottom };
433
434 SkScalar blurRadius = args.fBlurRadius;
435
436 // In the case where we have to inset more for the umbra, our two triangles in the
437 // corner get skewed to a diamond rather than a square. To correct for that,
438 // we also skew the vectors we send to the shader that help define the circle.
439 // By doing so, we end up with a quarter circle in the corner rather than the
440 // elliptical curve.
441
442 // This is a bit magical, but it gives us the correct results at extrema:
443 // a) umbraInset == outerRadius produces an orthogonal vector
444 // b) outerRadius == 0 produces a diagonal vector
445 // And visually the corner looks correct.
446 SkVector outerVec = SkVector::Make(outerRadius - umbraInset, -outerRadius - umbraInset);
447 outerVec.normalize();
448 // We want the circle edge to fall fractionally along the diagonal at
449 // (sqrt(2)*(umbraInset - outerRadius) + outerRadius)/sqrt(2)*umbraInset
450 //
451 // Setting the components of the diagonal offset to the following value will give us that.
452 SkScalar diagVal = umbraInset / (SK_ScalarSqrt2*(outerRadius - umbraInset) - outerRadius);
453 SkVector diagVec = SkVector::Make(diagVal, diagVal);
454 SkScalar distanceCorrection = umbraInset / blurRadius;
455
456 // build corner by corner
457 for (int i = 0; i < 4; ++i) {
458 // inner point
459 (*verts)->fPos = SkPoint::Make(xInner[i], yInner[i]);
460 (*verts)->fColor = color;
461 (*verts)->fOffset = SkVector::Make(0, 0);
462 (*verts)->fDistanceCorrection = distanceCorrection;
463 (*verts)++;
464
465 // outer points
466 (*verts)->fPos = SkPoint::Make(xOuter[i], yInner[i]);
467 (*verts)->fColor = color;
468 (*verts)->fOffset = SkVector::Make(0, -1);
469 (*verts)->fDistanceCorrection = distanceCorrection;
470 (*verts)++;
471
472 (*verts)->fPos = SkPoint::Make(xOuter[i], yMid[i]);
473 (*verts)->fColor = color;
474 (*verts)->fOffset = outerVec;
475 (*verts)->fDistanceCorrection = distanceCorrection;
476 (*verts)++;
477
478 (*verts)->fPos = SkPoint::Make(xOuter[i], yOuter[i]);
479 (*verts)->fColor = color;
480 (*verts)->fOffset = diagVec;
481 (*verts)->fDistanceCorrection = distanceCorrection;
482 (*verts)++;
483
484 (*verts)->fPos = SkPoint::Make(xMid[i], yOuter[i]);
485 (*verts)->fColor = color;
486 (*verts)->fOffset = outerVec;
487 (*verts)->fDistanceCorrection = distanceCorrection;
488 (*verts)++;
489
490 (*verts)->fPos = SkPoint::Make(xInner[i], yOuter[i]);
491 (*verts)->fColor = color;
492 (*verts)->fOffset = SkVector::Make(0, -1);
493 (*verts)->fDistanceCorrection = distanceCorrection;
494 (*verts)++;
495 }
496
497 // Add the additional vertices for overstroked rrects.
498 // Effectively this is an additional stroked rrect, with its
499 // parameters equal to those in the center of the 9-patch. This will
500 // give constant values across this inner ring.
501 if (kOverstroke_RRectType == args.fType) {
502 SkASSERT(args.fInnerRadius > 0.0f);
503
504 SkScalar inset = umbraInset + args.fInnerRadius;
505
506 // TL
507 (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fTop + inset);
508 (*verts)->fColor = color;
509 (*verts)->fOffset = SkPoint::Make(0, 0);
510 (*verts)->fDistanceCorrection = distanceCorrection;
511 (*verts)++;
512
513 // TR
514 (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fTop + inset);
515 (*verts)->fColor = color;
516 (*verts)->fOffset = SkPoint::Make(0, 0);
517 (*verts)->fDistanceCorrection = distanceCorrection;
518 (*verts)++;
519
520 // BL
521 (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fBottom - inset);
522 (*verts)->fColor = color;
523 (*verts)->fOffset = SkPoint::Make(0, 0);
524 (*verts)->fDistanceCorrection = distanceCorrection;
525 (*verts)++;
526
527 // BR
528 (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fBottom - inset);
529 (*verts)->fColor = color;
530 (*verts)->fOffset = SkPoint::Make(0, 0);
531 (*verts)->fDistanceCorrection = distanceCorrection;
532 (*verts)++;
533 }
534
535 }
536
onPrepareDraws(Target * target)537 void onPrepareDraws(Target* target) override {
538 // Setup geometry processor
539 sk_sp<GrGeometryProcessor> gp = GrRRectShadowGeoProc::Make();
540
541 int instanceCount = fGeoData.count();
542 SkASSERT(sizeof(CircleVertex) == gp->vertexStride());
543
544 sk_sp<const GrBuffer> vertexBuffer;
545 int firstVertex;
546 CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(
547 sizeof(CircleVertex), fVertCount, &vertexBuffer, &firstVertex);
548 if (!verts) {
549 SkDebugf("Could not allocate vertices\n");
550 return;
551 }
552
553 sk_sp<const GrBuffer> indexBuffer;
554 int firstIndex = 0;
555 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
556 if (!indices) {
557 SkDebugf("Could not allocate indices\n");
558 return;
559 }
560
561 int currStartVertex = 0;
562 for (int i = 0; i < instanceCount; i++) {
563 const Geometry& args = fGeoData[i];
564
565 if (args.fIsCircle) {
566 bool isStroked = SkToBool(kStroke_RRectType == args.fType);
567 this->fillInCircleVerts(args, isStroked, &verts);
568
569 const uint16_t* primIndices = circle_type_to_indices(isStroked);
570 const int primIndexCount = circle_type_to_index_count(isStroked);
571 for (int i = 0; i < primIndexCount; ++i) {
572 *indices++ = primIndices[i] + currStartVertex;
573 }
574
575 currStartVertex += circle_type_to_vert_count(isStroked);
576
577 } else {
578 this->fillInRRectVerts(args, &verts);
579
580 const uint16_t* primIndices = rrect_type_to_indices(args.fType);
581 const int primIndexCount = rrect_type_to_index_count(args.fType);
582 for (int i = 0; i < primIndexCount; ++i) {
583 *indices++ = primIndices[i] + currStartVertex;
584 }
585
586 currStartVertex += rrect_type_to_vert_count(args.fType);
587 }
588 }
589
590 GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
591 mesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
592 GrPrimitiveRestart::kNo);
593 mesh->setVertexData(std::move(vertexBuffer), firstVertex);
594 target->recordDraw(std::move(gp), mesh);
595 }
596
onExecute(GrOpFlushState * flushState,const SkRect & chainBounds)597 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
598 flushState->executeDrawsAndUploadsForMeshDrawOp(
599 this, chainBounds, GrProcessorSet::MakeEmptySet());
600 }
601
onCombineIfPossible(GrOp * t,const GrCaps & caps)602 CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
603 ShadowCircularRRectOp* that = t->cast<ShadowCircularRRectOp>();
604 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
605 fVertCount += that->fVertCount;
606 fIndexCount += that->fIndexCount;
607 return CombineResult::kMerged;
608 }
609
610 SkSTArray<1, Geometry, true> fGeoData;
611 int fVertCount;
612 int fIndexCount;
613
614 typedef GrMeshDrawOp INHERITED;
615 };
616
617 } // anonymous namespace
618
619 ///////////////////////////////////////////////////////////////////////////////
620
621 namespace GrShadowRRectOp {
Make(GrRecordingContext * context,GrColor color,const SkMatrix & viewMatrix,const SkRRect & rrect,SkScalar blurWidth,SkScalar insetWidth)622 std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
623 GrColor color,
624 const SkMatrix& viewMatrix,
625 const SkRRect& rrect,
626 SkScalar blurWidth,
627 SkScalar insetWidth) {
628 // Shadow rrect ops only handle simple circular rrects.
629 SkASSERT(viewMatrix.isSimilarity() && SkRRectPriv::EqualRadii(rrect));
630
631 // Do any matrix crunching before we reset the draw state for device coords.
632 const SkRect& rrectBounds = rrect.getBounds();
633 SkRect bounds;
634 viewMatrix.mapRect(&bounds, rrectBounds);
635
636 // Map radius and inset. As the matrix is a similarity matrix, this should be isotropic.
637 SkScalar radius = SkRRectPriv::GetSimpleRadii(rrect).fX;
638 SkScalar matrixFactor = viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewX];
639 SkScalar scaledRadius = SkScalarAbs(radius*matrixFactor);
640 SkScalar scaledInsetWidth = SkScalarAbs(insetWidth*matrixFactor);
641
642 if (scaledInsetWidth <= 0) {
643 return nullptr;
644 }
645
646 GrOpMemoryPool* pool = context->priv().opMemoryPool();
647
648 return pool->allocate<ShadowCircularRRectOp>(color, bounds,
649 scaledRadius,
650 rrect.isOval(),
651 blurWidth,
652 scaledInsetWidth);
653 }
654 }
655
656 ///////////////////////////////////////////////////////////////////////////////
657
658 #if GR_TEST_UTILS
659
GR_DRAW_OP_TEST_DEFINE(ShadowRRectOp)660 GR_DRAW_OP_TEST_DEFINE(ShadowRRectOp) {
661 // create a similarity matrix
662 SkScalar rotate = random->nextSScalar1() * 360.f;
663 SkScalar translateX = random->nextSScalar1() * 1000.f;
664 SkScalar translateY = random->nextSScalar1() * 1000.f;
665 SkScalar scale;
666 do {
667 scale = random->nextSScalar1() * 100.f;
668 } while (scale == 0);
669 SkMatrix viewMatrix;
670 viewMatrix.setRotate(rotate);
671 viewMatrix.postTranslate(translateX, translateY);
672 viewMatrix.postScale(scale, scale);
673 SkScalar insetWidth = random->nextSScalar1() * 72.f;
674 SkScalar blurWidth = random->nextSScalar1() * 72.f;
675 bool isCircle = random->nextBool();
676 // This op doesn't use a full GrPaint, just a color.
677 GrColor color = paint.getColor4f().toBytes_RGBA();
678 if (isCircle) {
679 SkRect circle = GrTest::TestSquare(random);
680 SkRRect rrect = SkRRect::MakeOval(circle);
681 return GrShadowRRectOp::Make(context, color, viewMatrix, rrect, blurWidth, insetWidth);
682 } else {
683 SkRRect rrect;
684 do {
685 // This may return a rrect with elliptical corners, which we don't support.
686 rrect = GrTest::TestRRectSimple(random);
687 } while (!SkRRectPriv::IsSimpleCircular(rrect));
688 return GrShadowRRectOp::Make(context, color, viewMatrix, rrect, blurWidth, insetWidth);
689 }
690 }
691
692 #endif
693