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