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