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