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 "GrDrawOpTest.h"
11 #include "GrOpFlushState.h"
12 #include "GrResourceProvider.h"
13 #include "GrStyle.h"
14
15 #include "effects/GrShadowGeoProc.h"
16
17 ///////////////////////////////////////////////////////////////////////////////
18
19 // We have two possible cases for geometry for a circle:
20
21 // In the case of a normal fill, we draw geometry for the circle as an octagon.
22 static const uint16_t gFillCircleIndices[] = {
23 // enter the octagon
24 // clang-format off
25 0, 1, 8, 1, 2, 8,
26 2, 3, 8, 3, 4, 8,
27 4, 5, 8, 5, 6, 8,
28 6, 7, 8, 7, 0, 8,
29 // clang-format on
30 };
31
32 // For stroked circles, we use two nested octagons.
33 static const uint16_t gStrokeCircleIndices[] = {
34 // enter the octagon
35 // clang-format off
36 0, 1, 9, 0, 9, 8,
37 1, 2, 10, 1, 10, 9,
38 2, 3, 11, 2, 11, 10,
39 3, 4, 12, 3, 12, 11,
40 4, 5, 13, 4, 13, 12,
41 5, 6, 14, 5, 14, 13,
42 6, 7, 15, 6, 15, 14,
43 7, 0, 8, 7, 8, 15,
44 // clang-format on
45 };
46
47 static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
48 static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
49 static const int kVertsPerStrokeCircle = 16;
50 static const int kVertsPerFillCircle = 9;
51
circle_type_to_vert_count(bool stroked)52 static int circle_type_to_vert_count(bool stroked) {
53 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
54 }
55
circle_type_to_index_count(bool stroked)56 static int circle_type_to_index_count(bool stroked) {
57 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
58 }
59
circle_type_to_indices(bool stroked)60 static const uint16_t* circle_type_to_indices(bool stroked) {
61 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
62 }
63
64 ///////////////////////////////////////////////////////////////////////////////
65
66 class ShadowCircleOp final : public GrMeshDrawOp {
67 public:
68 DEFINE_OP_CLASS_ID
69
Make(GrColor color,const SkMatrix & viewMatrix,SkPoint center,SkScalar radius,SkScalar blurRadius,const GrStyle & style)70 static std::unique_ptr<GrMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
71 SkPoint center, SkScalar radius, SkScalar blurRadius,
72 const GrStyle& style) {
73 SkASSERT(viewMatrix.isSimilarity());
74 const SkStrokeRec& stroke = style.strokeRec();
75 if (style.hasPathEffect()) {
76 return nullptr;
77 }
78 SkStrokeRec::Style recStyle = stroke.getStyle();
79
80 viewMatrix.mapPoints(¢er, 1);
81 radius = viewMatrix.mapRadius(radius);
82 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
83
84 bool isStrokeOnly =
85 SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
86 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
87
88 SkScalar innerRadius = -SK_ScalarHalf;
89 SkScalar outerRadius = radius;
90 SkScalar halfWidth = 0;
91 if (hasStroke) {
92 if (SkScalarNearlyZero(strokeWidth)) {
93 halfWidth = SK_ScalarHalf;
94 } else {
95 halfWidth = SkScalarHalf(strokeWidth);
96 }
97
98 outerRadius += halfWidth;
99 if (isStrokeOnly) {
100 innerRadius = radius - halfWidth;
101 }
102 }
103
104 bool stroked = isStrokeOnly && innerRadius > 0.0f;
105 std::unique_ptr<ShadowCircleOp> op(new ShadowCircleOp());
106 op->fViewMatrixIfUsingLocalCoords = viewMatrix;
107
108 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
109 center.fX + outerRadius, center.fY + outerRadius);
110
111 op->fCircles.emplace_back(
112 Circle{color, outerRadius, innerRadius, blurRadius, devBounds, stroked});
113
114 // Use the original radius and stroke radius for the bounds so that it does not include the
115 // AA bloat.
116 radius += halfWidth;
117 op->setBounds(
118 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
119 HasAABloat::kNo, IsZeroArea::kNo);
120 op->fVertCount = circle_type_to_vert_count(stroked);
121 op->fIndexCount = circle_type_to_index_count(stroked);
122 return std::move(op);
123 }
124
name() const125 const char* name() const override { return "ShadowCircleOp"; }
126
dumpInfo() const127 SkString dumpInfo() const override {
128 SkString string;
129 for (int i = 0; i < fCircles.count(); ++i) {
130 string.appendf(
131 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
132 "OuterRad: %.2f, InnerRad: %.2f, BlurRad: %.2f\n",
133 fCircles[i].fColor, fCircles[i].fDevBounds.fLeft, fCircles[i].fDevBounds.fTop,
134 fCircles[i].fDevBounds.fRight, fCircles[i].fDevBounds.fBottom,
135 fCircles[i].fOuterRadius, fCircles[i].fInnerRadius, fCircles[i].fBlurRadius);
136 }
137 string.append(DumpPipelineInfo(*this->pipeline()));
138 string.append(INHERITED::dumpInfo());
139 return string;
140 }
141
142 private:
ShadowCircleOp()143 ShadowCircleOp() : INHERITED(ClassID()) {}
144
getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor * color,GrPipelineAnalysisCoverage * coverage) const145 void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
146 GrPipelineAnalysisCoverage* coverage) const override {
147 color->setToConstant(fCircles[0].fColor);
148 *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
149 }
150
applyPipelineOptimizations(const GrPipelineOptimizations & optimizations)151 void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
152 optimizations.getOverrideColorIfSet(&fCircles[0].fColor);
153 if (!optimizations.readsLocalCoords()) {
154 fViewMatrixIfUsingLocalCoords.reset();
155 }
156 }
157
onPrepareDraws(Target * target) const158 void onPrepareDraws(Target* target) const override {
159 SkMatrix localMatrix;
160 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
161 return;
162 }
163
164 // Setup geometry processor
165 sk_sp<GrGeometryProcessor> gp(GrRRectShadowGeoProc::Make(localMatrix));
166
167 struct CircleVertex {
168 SkPoint fPos;
169 GrColor fColor;
170 SkPoint fOffset;
171 SkScalar fOuterRadius;
172 SkScalar fBlurRadius;
173 };
174
175 int instanceCount = fCircles.count();
176 size_t vertexStride = gp->getVertexStride();
177 SkASSERT(vertexStride == sizeof(CircleVertex));
178
179 const GrBuffer* vertexBuffer;
180 int firstVertex;
181 char* vertices = (char*)target->makeVertexSpace(vertexStride, fVertCount, &vertexBuffer,
182 &firstVertex);
183 if (!vertices) {
184 SkDebugf("Could not allocate vertices\n");
185 return;
186 }
187
188 const GrBuffer* indexBuffer = nullptr;
189 int firstIndex = 0;
190 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
191 if (!indices) {
192 SkDebugf("Could not allocate indices\n");
193 return;
194 }
195
196 int currStartVertex = 0;
197 for (int i = 0; i < instanceCount; i++) {
198 const Circle& circle = fCircles[i];
199
200 GrColor color = circle.fColor;
201 SkScalar outerRadius = circle.fOuterRadius;
202 SkScalar innerRadius = circle.fInnerRadius;
203 SkScalar blurRadius = circle.fBlurRadius;
204
205 const SkRect& bounds = circle.fDevBounds;
206 CircleVertex* ov0 = reinterpret_cast<CircleVertex*>(vertices + 0 * vertexStride);
207 CircleVertex* ov1 = reinterpret_cast<CircleVertex*>(vertices + 1 * vertexStride);
208 CircleVertex* ov2 = reinterpret_cast<CircleVertex*>(vertices + 2 * vertexStride);
209 CircleVertex* ov3 = reinterpret_cast<CircleVertex*>(vertices + 3 * vertexStride);
210 CircleVertex* ov4 = reinterpret_cast<CircleVertex*>(vertices + 4 * vertexStride);
211 CircleVertex* ov5 = reinterpret_cast<CircleVertex*>(vertices + 5 * vertexStride);
212 CircleVertex* ov6 = reinterpret_cast<CircleVertex*>(vertices + 6 * vertexStride);
213 CircleVertex* ov7 = reinterpret_cast<CircleVertex*>(vertices + 7 * vertexStride);
214
215 // The inner radius in the vertex data must be specified in normalized space.
216 innerRadius = innerRadius / outerRadius;
217
218 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
219 SkScalar halfWidth = 0.5f * bounds.width();
220 SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1
221
222 ov0->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth);
223 ov0->fColor = color;
224 ov0->fOffset = SkPoint::Make(-octOffset, -1);
225 ov0->fOuterRadius = outerRadius;
226 ov0->fBlurRadius = blurRadius;
227
228 ov1->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth);
229 ov1->fColor = color;
230 ov1->fOffset = SkPoint::Make(octOffset, -1);
231 ov1->fOuterRadius = outerRadius;
232 ov1->fBlurRadius = blurRadius;
233
234 ov2->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth);
235 ov2->fColor = color;
236 ov2->fOffset = SkPoint::Make(1, -octOffset);
237 ov2->fOuterRadius = outerRadius;
238 ov2->fBlurRadius = blurRadius;
239
240 ov3->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth);
241 ov3->fColor = color;
242 ov3->fOffset = SkPoint::Make(1, octOffset);
243 ov3->fOuterRadius = outerRadius;
244 ov3->fBlurRadius = blurRadius;
245
246 ov4->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth);
247 ov4->fColor = color;
248 ov4->fOffset = SkPoint::Make(octOffset, 1);
249 ov4->fOuterRadius = outerRadius;
250 ov4->fBlurRadius = blurRadius;
251
252 ov5->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth);
253 ov5->fColor = color;
254 ov5->fOffset = SkPoint::Make(-octOffset, 1);
255 ov5->fOuterRadius = outerRadius;
256 ov5->fBlurRadius = blurRadius;
257
258 ov6->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth);
259 ov6->fColor = color;
260 ov6->fOffset = SkPoint::Make(-1, octOffset);
261 ov6->fOuterRadius = outerRadius;
262 ov6->fBlurRadius = blurRadius;
263
264 ov7->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth);
265 ov7->fColor = color;
266 ov7->fOffset = SkPoint::Make(-1, -octOffset);
267 ov7->fOuterRadius = outerRadius;
268 ov7->fBlurRadius = blurRadius;
269
270 if (circle.fStroked) {
271 // compute the inner ring
272 CircleVertex* iv0 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
273 CircleVertex* iv1 = reinterpret_cast<CircleVertex*>(vertices + 9 * vertexStride);
274 CircleVertex* iv2 = reinterpret_cast<CircleVertex*>(vertices + 10 * vertexStride);
275 CircleVertex* iv3 = reinterpret_cast<CircleVertex*>(vertices + 11 * vertexStride);
276 CircleVertex* iv4 = reinterpret_cast<CircleVertex*>(vertices + 12 * vertexStride);
277 CircleVertex* iv5 = reinterpret_cast<CircleVertex*>(vertices + 13 * vertexStride);
278 CircleVertex* iv6 = reinterpret_cast<CircleVertex*>(vertices + 14 * vertexStride);
279 CircleVertex* iv7 = reinterpret_cast<CircleVertex*>(vertices + 15 * vertexStride);
280
281 // cosine and sine of pi/8
282 SkScalar c = 0.923579533f;
283 SkScalar s = 0.382683432f;
284 SkScalar r = circle.fInnerRadius;
285
286 iv0->fPos = center + SkPoint::Make(-s * r, -c * r);
287 iv0->fColor = color;
288 iv0->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius);
289 iv0->fOuterRadius = outerRadius;
290 iv0->fBlurRadius = blurRadius;
291
292 iv1->fPos = center + SkPoint::Make(s * r, -c * r);
293 iv1->fColor = color;
294 iv1->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius);
295 iv1->fOuterRadius = outerRadius;
296 iv1->fBlurRadius = blurRadius;
297
298 iv2->fPos = center + SkPoint::Make(c * r, -s * r);
299 iv2->fColor = color;
300 iv2->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius);
301 iv2->fOuterRadius = outerRadius;
302 iv2->fBlurRadius = blurRadius;
303
304 iv3->fPos = center + SkPoint::Make(c * r, s * r);
305 iv3->fColor = color;
306 iv3->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius);
307 iv3->fOuterRadius = outerRadius;
308 iv3->fBlurRadius = blurRadius;
309
310 iv4->fPos = center + SkPoint::Make(s * r, c * r);
311 iv4->fColor = color;
312 iv4->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius);
313 iv4->fOuterRadius = outerRadius;
314 iv4->fBlurRadius = blurRadius;
315
316 iv5->fPos = center + SkPoint::Make(-s * r, c * r);
317 iv5->fColor = color;
318 iv5->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius);
319 iv5->fOuterRadius = outerRadius;
320 iv5->fBlurRadius = blurRadius;
321
322 iv6->fPos = center + SkPoint::Make(-c * r, s * r);
323 iv6->fColor = color;
324 iv6->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius);
325 iv6->fOuterRadius = outerRadius;
326 iv6->fBlurRadius = blurRadius;
327
328 iv7->fPos = center + SkPoint::Make(-c * r, -s * r);
329 iv7->fColor = color;
330 iv7->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius);
331 iv7->fOuterRadius = outerRadius;
332 iv7->fBlurRadius = blurRadius;
333 } else {
334 // filled
335 CircleVertex* iv = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
336 iv->fPos = center;
337 iv->fColor = color;
338 iv->fOffset = SkPoint::Make(0, 0);
339 iv->fOuterRadius = outerRadius;
340 iv->fBlurRadius = blurRadius;
341 }
342
343 const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
344 const int primIndexCount = circle_type_to_index_count(circle.fStroked);
345 for (int i = 0; i < primIndexCount; ++i) {
346 *indices++ = primIndices[i] + currStartVertex;
347 }
348
349 currStartVertex += circle_type_to_vert_count(circle.fStroked);
350 vertices += circle_type_to_vert_count(circle.fStroked) * vertexStride;
351 }
352
353 GrMesh mesh;
354 mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
355 firstIndex, fVertCount, fIndexCount);
356 target->draw(gp.get(), mesh);
357 }
358
onCombineIfPossible(GrOp * t,const GrCaps & caps)359 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
360 ShadowCircleOp* that = t->cast<ShadowCircleOp>();
361 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
362 that->bounds(), caps)) {
363 return false;
364 }
365
366 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
367 return false;
368 }
369
370 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
371 this->joinBounds(*that);
372 fVertCount += that->fVertCount;
373 fIndexCount += that->fIndexCount;
374 return true;
375 }
376
377 struct Circle {
378 GrColor fColor;
379 SkScalar fOuterRadius;
380 SkScalar fInnerRadius;
381 SkScalar fBlurRadius;
382 SkRect fDevBounds;
383 bool fStroked;
384 };
385
386 SkSTArray<1, Circle, true> fCircles;
387 SkMatrix fViewMatrixIfUsingLocalCoords;
388 int fVertCount;
389 int fIndexCount;
390
391 typedef GrMeshDrawOp INHERITED;
392 };
393
394 ///////////////////////////////////////////////////////////////////////////////////////////////////
395
396 // We have two possible cases for geometry for a shadow roundrect.
397 //
398 // In the case of a normal stroke, we draw the roundrect as a 9-patch without the center quad.
399 // ____________
400 // |_|________|_|
401 // | | | |
402 // | | | |
403 // | | | |
404 // |_|________|_|
405 // |_|________|_|
406 //
407 // In the case where the stroke width is greater than twice the corner radius (overstroke),
408 // we add additional geometry to mark out the rectangle in the center. The shared vertices
409 // are duplicated so we can set a different outer radius for the fill calculation.
410 // ____________
411 // |_|________|_|
412 // | |\ ____ /| |
413 // | | | | | |
414 // | | |____| | |
415 // |_|/______\|_|
416 // |_|________|_|
417 //
418 // For filled rrects we reuse the overstroke geometry but make the inner rect degenerate
419 // (either a point or a horizontal or vertical line).
420
421 static const uint16_t gOverstrokeRRectIndices[] = {
422 // clang-format off
423 // corners
424 0, 1, 5, 0, 5, 4,
425 2, 3, 7, 2, 7, 6,
426 8, 9, 13, 8, 13, 12,
427 10, 11, 15, 10, 15, 14,
428
429 // edges
430 1, 2, 6, 1, 6, 5,
431 4, 5, 9, 4, 9, 8,
432 6, 7, 11, 6, 11, 10,
433 9, 10, 14, 9, 14, 13,
434
435 // overstroke quads
436 // we place this at the end so that we can skip these indices when rendering as stroked
437 16, 17, 19, 16, 19, 18,
438 19, 17, 23, 19, 23, 21,
439 21, 23, 22, 21, 22, 20,
440 22, 16, 18, 22, 18, 20,
441 // clang-format on
442 };
443 // standard stroke indices start at the same place, but will skip the overstroke "ring"
444 static const uint16_t* gStrokeRRectIndices = gOverstrokeRRectIndices;
445
446 // overstroke count
447 static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices);
448 // simple stroke count skips overstroke indices
449 static const int kIndicesPerStrokeRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
450 static const int kVertsPerStrokeRRect = 16;
451 static const int kVertsPerOverstrokeRRect = 24;
452
453 enum RRectType {
454 kFill_RRectType,
455 kStroke_RRectType,
456 kOverstroke_RRectType,
457 };
458
rrect_type_to_vert_count(RRectType type)459 static int rrect_type_to_vert_count(RRectType type) {
460 switch (type) {
461 case kFill_RRectType:
462 return kVertsPerOverstrokeRRect;
463 case kStroke_RRectType:
464 return kVertsPerStrokeRRect;
465 case kOverstroke_RRectType:
466 return kVertsPerOverstrokeRRect;
467 }
468 SkFAIL("Invalid type");
469 return 0;
470 }
471
rrect_type_to_index_count(RRectType type)472 static int rrect_type_to_index_count(RRectType type) {
473 switch (type) {
474 case kFill_RRectType:
475 return kIndicesPerOverstrokeRRect;
476 case kStroke_RRectType:
477 return kIndicesPerStrokeRRect;
478 case kOverstroke_RRectType:
479 return kIndicesPerOverstrokeRRect;
480 }
481 SkFAIL("Invalid type");
482 return 0;
483 }
484
rrect_type_to_indices(RRectType type)485 static const uint16_t* rrect_type_to_indices(RRectType type) {
486 switch (type) {
487 case kFill_RRectType:
488 return gOverstrokeRRectIndices;
489 case kStroke_RRectType:
490 return gStrokeRRectIndices;
491 case kOverstroke_RRectType:
492 return gOverstrokeRRectIndices;
493 }
494 SkFAIL("Invalid type");
495 return nullptr;
496 }
497
498 // For distance computations in the interior of filled rrects we:
499 //
500 // add a interior degenerate (point or line) rect
501 // each vertex of that rect gets -outerRad as its radius
502 // this makes the computation of the distance to the outer edge be negative
503 // negative values are caught and then handled differently in the GP's onEmitCode
504 // each vertex is also given the normalized x & y distance from the interior rect's edge
505 // the GP takes the min of those depths +1 to get the normalized distance to the outer edge
506
507 class ShadowCircularRRectOp final : public GrMeshDrawOp {
508 public:
509 DEFINE_OP_CLASS_ID
510
511 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
512 // whether the rrect is only stroked or stroked and filled.
ShadowCircularRRectOp(GrColor color,const SkMatrix & viewMatrix,const SkRect & devRect,float devRadius,float blurRadius,float devStrokeWidth,bool strokeOnly)513 ShadowCircularRRectOp(GrColor color, const SkMatrix& viewMatrix, const SkRect& devRect,
514 float devRadius, float blurRadius, float devStrokeWidth, bool strokeOnly)
515 : INHERITED(ClassID()), fViewMatrixIfUsingLocalCoords(viewMatrix) {
516 SkRect bounds = devRect;
517 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
518 SkScalar innerRadius = 0.0f;
519 SkScalar outerRadius = devRadius;
520 SkScalar halfWidth = 0;
521 RRectType type = kFill_RRectType;
522 if (devStrokeWidth > 0) {
523 if (SkScalarNearlyZero(devStrokeWidth)) {
524 halfWidth = SK_ScalarHalf;
525 } else {
526 halfWidth = SkScalarHalf(devStrokeWidth);
527 }
528
529 if (strokeOnly) {
530 // If stroke is greater than width or height, this is still a fill
531 // Otherwise we compute stroke params
532 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
533 innerRadius = devRadius - halfWidth;
534 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
535 }
536 }
537 outerRadius += halfWidth;
538 bounds.outset(halfWidth, halfWidth);
539 }
540
541 this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
542
543 fGeoData.emplace_back(Geometry{color, outerRadius, innerRadius, blurRadius, bounds, type});
544 fVertCount = rrect_type_to_vert_count(type);
545 fIndexCount = rrect_type_to_index_count(type);
546 }
547
name() const548 const char* name() const override { return "ShadowCircularRRectOp"; }
549
dumpInfo() const550 SkString dumpInfo() const override {
551 SkString string;
552 for (int i = 0; i < fGeoData.count(); ++i) {
553 string.appendf(
554 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
555 "OuterRad: %.2f, InnerRad: %.2f, BlurRad: %.2f\n",
556 fGeoData[i].fColor, fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
557 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
558 fGeoData[i].fOuterRadius, fGeoData[i].fInnerRadius, fGeoData[i].fBlurRadius);
559 }
560 string.append(DumpPipelineInfo(*this->pipeline()));
561 string.append(INHERITED::dumpInfo());
562 return string;
563 }
564
565 private:
getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor * color,GrPipelineAnalysisCoverage * coverage) const566 void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
567 GrPipelineAnalysisCoverage* coverage) const override {
568 color->setToConstant(fGeoData[0].fColor);
569 *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
570 }
571
applyPipelineOptimizations(const GrPipelineOptimizations & optimizations)572 void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
573 optimizations.getOverrideColorIfSet(&fGeoData[0].fColor);
574 if (!optimizations.readsLocalCoords()) {
575 fViewMatrixIfUsingLocalCoords.reset();
576 }
577 }
578
579 struct CircleVertex {
580 SkPoint fPos;
581 GrColor fColor;
582 SkPoint fOffset;
583 SkScalar fOuterRadius;
584 SkScalar fBlurRadius;
585 };
586
FillInOverstrokeVerts(CircleVertex ** verts,const SkRect & bounds,SkScalar smInset,SkScalar bigInset,SkScalar xOffset,SkScalar outerRadius,GrColor color,SkScalar blurRadius)587 static void FillInOverstrokeVerts(CircleVertex** verts, const SkRect& bounds, SkScalar smInset,
588 SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
589 GrColor color, SkScalar blurRadius) {
590 SkASSERT(smInset < bigInset);
591
592 // TL
593 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fTop + smInset);
594 (*verts)->fColor = color;
595 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
596 (*verts)->fOuterRadius = outerRadius;
597 (*verts)->fBlurRadius = blurRadius;
598 (*verts)++;
599
600 // TR
601 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fTop + smInset);
602 (*verts)->fColor = color;
603 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
604 (*verts)->fOuterRadius = outerRadius;
605 (*verts)->fBlurRadius = blurRadius;
606 (*verts)++;
607
608 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fTop + bigInset);
609 (*verts)->fColor = color;
610 (*verts)->fOffset = SkPoint::Make(0, 0);
611 (*verts)->fOuterRadius = outerRadius;
612 (*verts)->fBlurRadius = blurRadius;
613 (*verts)++;
614
615 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fTop + bigInset);
616 (*verts)->fColor = color;
617 (*verts)->fOffset = SkPoint::Make(0, 0);
618 (*verts)->fOuterRadius = outerRadius;
619 (*verts)->fBlurRadius = blurRadius;
620 (*verts)++;
621
622 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fBottom - bigInset);
623 (*verts)->fColor = color;
624 (*verts)->fOffset = SkPoint::Make(0, 0);
625 (*verts)->fOuterRadius = outerRadius;
626 (*verts)->fBlurRadius = blurRadius;
627 (*verts)++;
628
629 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fBottom - bigInset);
630 (*verts)->fColor = color;
631 (*verts)->fOffset = SkPoint::Make(0, 0);
632 (*verts)->fOuterRadius = outerRadius;
633 (*verts)->fBlurRadius = blurRadius;
634 (*verts)++;
635
636 // BL
637 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fBottom - smInset);
638 (*verts)->fColor = color;
639 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
640 (*verts)->fOuterRadius = outerRadius;
641 (*verts)->fBlurRadius = blurRadius;
642 (*verts)++;
643
644 // BR
645 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fBottom - smInset);
646 (*verts)->fColor = color;
647 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
648 (*verts)->fOuterRadius = outerRadius;
649 (*verts)->fBlurRadius = blurRadius;
650 (*verts)++;
651 }
652
onPrepareDraws(Target * target) const653 void onPrepareDraws(Target* target) const override {
654 // Invert the view matrix as a local matrix (if any other processors require coords).
655 SkMatrix localMatrix;
656 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
657 return;
658 }
659
660 // Setup geometry processor
661 sk_sp<GrGeometryProcessor> gp(GrRRectShadowGeoProc::Make(localMatrix));
662
663 int instanceCount = fGeoData.count();
664 size_t vertexStride = gp->getVertexStride();
665 SkASSERT(sizeof(CircleVertex) == vertexStride);
666
667 const GrBuffer* vertexBuffer;
668 int firstVertex;
669
670 CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(vertexStride, fVertCount,
671 &vertexBuffer, &firstVertex);
672 if (!verts) {
673 SkDebugf("Could not allocate vertices\n");
674 return;
675 }
676
677 const GrBuffer* indexBuffer = nullptr;
678 int firstIndex = 0;
679 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
680 if (!indices) {
681 SkDebugf("Could not allocate indices\n");
682 return;
683 }
684
685 int currStartVertex = 0;
686 for (int i = 0; i < instanceCount; i++) {
687 const Geometry& args = fGeoData[i];
688
689 GrColor color = args.fColor;
690 SkScalar outerRadius = args.fOuterRadius;
691
692 const SkRect& bounds = args.fDevBounds;
693
694 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
695 bounds.fBottom - outerRadius, bounds.fBottom};
696
697 SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
698 // The inner radius in the vertex data must be specified in normalized space.
699 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
700 SkScalar blurRadius = args.fBlurRadius;
701 for (int i = 0; i < 4; ++i) {
702 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
703 verts->fColor = color;
704 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
705 verts->fOuterRadius = outerRadius;
706 verts->fBlurRadius = blurRadius;
707 verts++;
708
709 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
710 verts->fColor = color;
711 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
712 verts->fOuterRadius = outerRadius;
713 verts->fBlurRadius = blurRadius;
714 verts++;
715
716 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
717 verts->fColor = color;
718 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
719 verts->fOuterRadius = outerRadius;
720 verts->fBlurRadius = blurRadius;
721 verts++;
722
723 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
724 verts->fColor = color;
725 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
726 verts->fOuterRadius = outerRadius;
727 verts->fBlurRadius = blurRadius;
728 verts++;
729 }
730 // Add the additional vertices for overstroked rrects.
731 // Effectively this is an additional stroked rrect, with its
732 // outer radius = outerRadius - innerRadius, and inner radius = 0.
733 // This will give us correct AA in the center and the correct
734 // distance to the outer edge.
735 //
736 // Also, the outer offset is a constant vector pointing to the right, which
737 // guarantees that the distance value along the outer rectangle is constant.
738 if (kOverstroke_RRectType == args.fType) {
739 SkASSERT(args.fInnerRadius <= 0.0f);
740
741 SkScalar overstrokeOuterRadius = outerRadius - args.fInnerRadius;
742 // this is the normalized distance from the outer rectangle of this
743 // geometry to the outer edge
744 SkScalar maxOffset = -args.fInnerRadius / overstrokeOuterRadius;
745
746 FillInOverstrokeVerts(&verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
747 overstrokeOuterRadius, color, blurRadius);
748 }
749
750 if (kFill_RRectType == args.fType) {
751 SkScalar halfMinDim = 0.5f * SkTMin(bounds.width(), bounds.height());
752
753 SkScalar xOffset = 1.0f - outerRadius / halfMinDim;
754
755 FillInOverstrokeVerts(&verts, bounds, outerRadius, halfMinDim, xOffset, halfMinDim,
756 color, blurRadius);
757 }
758
759 const uint16_t* primIndices = rrect_type_to_indices(args.fType);
760 const int primIndexCount = rrect_type_to_index_count(args.fType);
761 for (int i = 0; i < primIndexCount; ++i) {
762 *indices++ = primIndices[i] + currStartVertex;
763 }
764
765 currStartVertex += rrect_type_to_vert_count(args.fType);
766 }
767
768 GrMesh mesh;
769 mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
770 firstIndex, fVertCount, fIndexCount);
771 target->draw(gp.get(), mesh);
772 }
773
onCombineIfPossible(GrOp * t,const GrCaps & caps)774 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
775 ShadowCircularRRectOp* that = t->cast<ShadowCircularRRectOp>();
776 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
777 that->bounds(), caps)) {
778 return false;
779 }
780
781 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
782 return false;
783 }
784
785 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
786 this->joinBounds(*that);
787 fVertCount += that->fVertCount;
788 fIndexCount += that->fIndexCount;
789 return true;
790 }
791
792 struct Geometry {
793 GrColor fColor;
794 SkScalar fOuterRadius;
795 SkScalar fInnerRadius;
796 SkScalar fBlurRadius;
797 SkRect fDevBounds;
798 RRectType fType;
799 };
800
801 SkSTArray<1, Geometry, true> fGeoData;
802 SkMatrix fViewMatrixIfUsingLocalCoords;
803 int fVertCount;
804 int fIndexCount;
805
806 typedef GrMeshDrawOp INHERITED;
807 };
808
809 ///////////////////////////////////////////////////////////////////////////////
810
make_shadow_circle_op(GrColor color,const SkMatrix & viewMatrix,const SkRect & oval,SkScalar blurRadius,const SkStrokeRec & stroke,const GrShaderCaps * shaderCaps)811 std::unique_ptr<GrMeshDrawOp> make_shadow_circle_op(GrColor color,
812 const SkMatrix& viewMatrix,
813 const SkRect& oval,
814 SkScalar blurRadius,
815 const SkStrokeRec& stroke,
816 const GrShaderCaps* shaderCaps) {
817 // we can only draw circles
818 SkScalar width = oval.width();
819 SkASSERT(SkScalarNearlyEqual(width, oval.height()) && viewMatrix.isSimilarity());
820 SkPoint center = {oval.centerX(), oval.centerY()};
821 return ShadowCircleOp::Make(color, viewMatrix, center, width / 2.f, blurRadius,
822 GrStyle(stroke, nullptr));
823 }
824
make_shadow_rrect_op(GrColor color,const SkMatrix & viewMatrix,const SkRRect & rrect,SkScalar blurRadius,const SkStrokeRec & stroke)825 static std::unique_ptr<GrMeshDrawOp> make_shadow_rrect_op(GrColor color,
826 const SkMatrix& viewMatrix,
827 const SkRRect& rrect,
828 SkScalar blurRadius,
829 const SkStrokeRec& stroke) {
830 SkASSERT(viewMatrix.rectStaysRect());
831 SkASSERT(rrect.isSimple());
832 SkASSERT(!rrect.isOval());
833
834 // Shadow rrect ops only handle simple circular rrects.
835 // Do any matrix crunching before we reset the draw state for device coords.
836 const SkRect& rrectBounds = rrect.getBounds();
837 SkRect bounds;
838 viewMatrix.mapRect(&bounds, rrectBounds);
839
840 SkVector radii = rrect.getSimpleRadii();
841 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
842 viewMatrix[SkMatrix::kMSkewY] * radii.fY);
843 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
844 viewMatrix[SkMatrix::kMScaleY] * radii.fY);
845 SkASSERT(SkScalarNearlyEqual(xRadius, yRadius));
846
847 SkStrokeRec::Style style = stroke.getStyle();
848
849 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
850 SkVector scaledStroke = {-1, -1};
851 SkScalar strokeWidth = stroke.getWidth();
852
853 bool isStrokeOnly =
854 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
855 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
856
857 if (hasStroke) {
858 if (SkStrokeRec::kHairline_Style == style) {
859 scaledStroke.set(1, 1);
860 } else {
861 scaledStroke.fX = SkScalarAbs(
862 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
863 scaledStroke.fY = SkScalarAbs(
864 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
865 }
866
867 // we don't handle anisotropic strokes
868 if (!SkScalarNearlyEqual(scaledStroke.fX, scaledStroke.fY)) {
869 return nullptr;
870 }
871 }
872
873 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
874 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
875 // patch will have fractional coverage. This only matters when the interior is actually filled.
876 // We could consider falling back to rect rendering here, since a tiny radius is
877 // indistinguishable from a square corner.
878 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
879 return nullptr;
880 }
881
882 return std::unique_ptr<GrMeshDrawOp>(new ShadowCircularRRectOp(
883 color, viewMatrix, bounds, xRadius, blurRadius, scaledStroke.fX, isStrokeOnly));
884 }
885
886 namespace GrShadowRRectOp {
Make(GrColor color,const SkMatrix & viewMatrix,const SkRRect & rrect,const SkScalar blurRadius,const SkStrokeRec & stroke,const GrShaderCaps * shaderCaps)887 std::unique_ptr<GrMeshDrawOp> Make(GrColor color,
888 const SkMatrix& viewMatrix,
889 const SkRRect& rrect,
890 const SkScalar blurRadius,
891 const SkStrokeRec& stroke,
892 const GrShaderCaps* shaderCaps) {
893 if (rrect.isOval()) {
894 return make_shadow_circle_op(color, viewMatrix, rrect.getBounds(), blurRadius, stroke,
895 shaderCaps);
896 }
897
898 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
899 return nullptr;
900 }
901
902 return make_shadow_rrect_op(color, viewMatrix, rrect, blurRadius, stroke);
903 }
904 }
905 ///////////////////////////////////////////////////////////////////////////////
906
907 #if GR_TEST_UTILS
908
DRAW_OP_TEST_DEFINE(ShadowCircleOp)909 DRAW_OP_TEST_DEFINE(ShadowCircleOp) {
910 do {
911 SkScalar rotate = random->nextSScalar1() * 360.f;
912 SkScalar translateX = random->nextSScalar1() * 1000.f;
913 SkScalar translateY = random->nextSScalar1() * 1000.f;
914 SkScalar scale = random->nextSScalar1() * 100.f;
915 SkMatrix viewMatrix;
916 viewMatrix.setRotate(rotate);
917 viewMatrix.postTranslate(translateX, translateY);
918 viewMatrix.postScale(scale, scale);
919 GrColor color = GrRandomColor(random);
920 SkRect circle = GrTest::TestSquare(random);
921 SkPoint center = {circle.centerX(), circle.centerY()};
922 SkScalar radius = circle.width() / 2.f;
923 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
924 SkScalar blurRadius = random->nextSScalar1() * 72.f;
925 std::unique_ptr<GrMeshDrawOp> op = ShadowCircleOp::Make(
926 color, viewMatrix, center, radius, blurRadius, GrStyle(stroke, nullptr));
927 if (op) {
928 return op;
929 }
930 } while (true);
931 }
932
DRAW_OP_TEST_DEFINE(ShadowRRectOp)933 DRAW_OP_TEST_DEFINE(ShadowRRectOp) {
934 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
935 GrColor color = GrRandomColor(random);
936 const SkRRect& rrect = GrTest::TestRRectSimple(random);
937 SkScalar blurRadius = random->nextSScalar1() * 72.f;
938 return make_shadow_rrect_op(color, viewMatrix, rrect, blurRadius,
939 GrTest::TestStrokeRec(random));
940 }
941
942 #endif
943