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