Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
ShadowRRectOp.cpp
Go to the documentation of this file.
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
9
19#include "src/gpu/ganesh/SkGr.h"
22
23using namespace skia_private;
24
25namespace {
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.
33static 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.
44static 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
58static const int kIndicesPerFillCircle = std::size(gFillCircleIndices);
59static const int kIndicesPerStrokeCircle = std::size(gStrokeCircleIndices);
60static const int kVertsPerStrokeCircle = 16;
61static const int kVertsPerFillCircle = 9;
62
63int circle_type_to_vert_count(bool stroked) {
65}
66
67int circle_type_to_index_count(bool stroked) {
69}
70
71const uint16_t* circle_type_to_indices(bool stroked) {
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
114static 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
142static const int kIndicesPerOverstrokeRRect = std::size(gRRectIndices) - 6;
143// simple stroke count skips overstroke indices
145// fill count adds final quad to stroke count
146static const int kIndicesPerFillRRect = kIndicesPerStrokeRRect + 6;
147static const int kVertsPerStrokeRRect = 24;
148static const int kVertsPerOverstrokeRRect = 28;
149static const int kVertsPerFillRRect = 24;
150
151enum RRectType {
155};
156
158 switch (type) {
159 case kFill_RRectType:
160 return kVertsPerFillRRect;
162 return kVertsPerStrokeRRect;
165 }
166 SK_ABORT("Invalid type");
167}
168
170 switch (type) {
171 case kFill_RRectType:
177 }
178 SK_ABORT("Invalid type");
179}
180
181const uint16_t* rrect_type_to_indices(RRectType type) {
182 switch (type) {
183 case kFill_RRectType:
185 return gRRectIndices + 6*4;
187 return gRRectIndices;
188 }
189 SK_ABORT("Invalid type");
190}
191
192///////////////////////////////////////////////////////////////////////////////
193
194class ShadowCircularRRectOp final : public GrMeshDrawOp {
195public:
197
198 // An insetWidth > 1/2 rect width or height indicates a simple fill.
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
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) {
238 } else {
239 fVertCount = rrect_type_to_vert_count(type);
240 fIndexCount = rrect_type_to_index_count(type);
241 }
242 }
243
244 const char* name() const override { return "ShadowCircularRRectOp"; }
245
246 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
247
250 }
251
252private:
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
271 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
401 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
529 GrProgramInfo* programInfo() override { return fProgramInfo; }
530
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,
548 renderPassXferBarriers,
549 colorLoadOp,
552 }
553
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
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
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)
632 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
647 void visitProxies(const GrVisitProxyFunc& func) const override {
648 func(fFalloffView.proxy(), skgpu::Mipmapped(false));
649 if (fProgramInfo) {
650 fProgramInfo->visitFPProxies(func);
651 }
652 }
653
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
670
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) {
682 return view;
683 }
684
685 static const int kWidth = 128;
686 static const size_t kRowBytes = kWidth * GrColorTypeBytesPerPixel(GrColorType::kAlpha_8);
688
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);
706 return view;
707}
708
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.
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
755
756GR_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);
778 context, color, viewMatrix, rrect, blurWidth, insetWidth)) {
779 return op;
780 }
781 } else {
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));
788 context, color, viewMatrix, rrect, blurWidth, insetWidth)) {
789 return op;
790 }
791 }
792 } while (true);
793}
794
795#endif // defined(GR_TEST_UTILS)
uint32_t GrColor
Definition GrColor.h:25
#define DEFINE_OP_CLASS_ID
Definition GrOp.h:64
static const uint16_t * circle_type_to_indices(bool stroked)
static int rrect_type_to_vert_count(RRectType type)
static int rrect_type_to_index_count(RRectType type)
static int circle_type_to_index_count(bool stroked)
static const int kVertsPerFillCircle
static const uint16_t gStrokeCircleIndices[]
static const int kIndicesPerFillRRect
@ kOverstroke_RRectType
@ kFill_RRectType
@ kStroke_RRectType
static const int kIndicesPerStrokeCircle
static const uint16_t gFillCircleIndices[]
static const int kIndicesPerOverstrokeRRect
static const int kIndicesPerFillCircle
static const int kVertsPerOverstrokeRRect
static const uint16_t * rrect_type_to_indices(RRectType type)
static const int kIndicesPerStrokeRRect
static const int kVertsPerStrokeCircle
static int circle_type_to_vert_count(bool stroked)
GrClampType
static constexpr size_t GrColorTypeBytesPerPixel(GrColorType ct)
std::function< void(GrSurfaceProxy *, skgpu::Mipmapped)> GrVisitProxyFunc
GrLoadOp
@ kTopLeft_GrSurfaceOrigin
Definition GrTypes.h:148
GrXferBarrierFlags
SkColor4f color
#define SK_ABORT(message,...)
Definition SkAssert.h:70
#define SkASSERT(cond)
Definition SkAssert.h:116
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
std::tuple< GrSurfaceProxyView, GrColorType > GrMakeUncachedBitmapProxyView(GrRecordingContext *rContext, const SkBitmap &bitmap, skgpu::Mipmapped mipmapped, SkBackingFit fit, skgpu::Budgeted budgeted)
Definition SkGr.cpp:253
static bool rotate(const SkDCubic &cubic, int zero, int index, SkDCubic &rotPath)
#define INHERITED(method,...)
#define SkScalarExp(x)
Definition SkScalar.h:51
#define SK_Scalar1
Definition SkScalar.h:18
#define SkScalarRoundToInt(x)
Definition SkScalar.h:37
#define SkIntToScalar(x)
Definition SkScalar.h:57
#define SK_ScalarSqrt2
Definition SkScalar.h:20
#define SkScalarAbs(x)
Definition SkScalar.h:39
static constexpr bool SkToBool(const T &x)
Definition SkTo.h:35
static SkScalar center(float pos0, float pos1)
virtual FixedFunctionFlags fixedFunctionFlags() const
Definition GrDrawOp.h:112
virtual GrProcessorSet::Analysis finalize(const GrCaps &, const GrAppliedClip *, GrClampType)=0
virtual GrProgramInfo * programInfo()=0
void createProgramInfo(const GrCaps *caps, SkArenaAlloc *arena, const GrSurfaceProxyView &writeView, bool usesMSAASurface, GrAppliedClip &&appliedClip, const GrDstProxyView &dstProxyView, GrXferBarrierFlags renderPassXferBarriers, GrLoadOp colorLoadOp)
virtual void onCreateProgramInfo(const GrCaps *, SkArenaAlloc *, const GrSurfaceProxyView &writeView, bool usesMSAASurface, GrAppliedClip &&, const GrDstProxyView &, GrXferBarrierFlags renderPassXferBarriers, GrLoadOp colorLoadOp)=0
virtual void onPrepareDraws(GrMeshDrawTarget *)=0
void drawMesh(const GrSimpleMesh &mesh)
void bindPipelineAndScissorClip(const GrProgramInfo &programInfo, const SkRect &drawBounds)
void bindTextures(const GrGeometryProcessor &geomProc, const GrSurfaceProxy &singleGeomProcTexture, const GrPipeline &pipeline)
Definition GrOp.h:70
virtual void onExecute(GrOpFlushState *, const SkRect &chainBounds)=0
std::unique_ptr< GrOp > Owner
Definition GrOp.h:72
virtual const char * name() const =0
const T & cast() const
Definition GrOp.h:148
const SkRect & bounds() const
Definition GrOp.h:122
void setBounds(const SkRect &newBounds, HasAABloat aabloat, IsHairline zeroArea)
Definition GrOp.h:279
virtual CombineResult onCombineIfPossible(GrOp *, SkArenaAlloc *, const GrCaps &)
Definition GrOp.h:305
static constexpr Analysis EmptySetAnalysis()
static GrProcessorSet MakeEmptySet()
const GrPipeline & pipeline() const
const GrGeometryProcessor & geomProc() const
void visitFPProxies(const GrVisitProxyFunc &func) const
static GrGeometryProcessor * Make(SkArenaAlloc *arena, const GrSurfaceProxyView &lutView)
GrThreadSafeCache * threadSafeCache()
GrRecordingContextPriv priv()
static GrProgramInfo * CreateProgramInfo(const GrCaps *, SkArenaAlloc *, const GrPipeline *, const GrSurfaceProxyView &writeView, bool usesMSAASurface, GrGeometryProcessor *, GrPrimitiveType, GrXferBarrierFlags renderPassXferBarriers, GrLoadOp colorLoadOp, const GrUserStencilSettings *=&GrUserStencilSettings::kUnused)
GrSurfaceOrigin origin() const
SkMatrix & postTranslate(SkScalar dx, SkScalar dy)
Definition SkMatrix.cpp:281
static constexpr int kMScaleX
horizontal scale factor
Definition SkMatrix.h:353
SkMatrix & postScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py)
Definition SkMatrix.cpp:360
SkMatrix & setRotate(SkScalar degrees, SkScalar px, SkScalar py)
Definition SkMatrix.cpp:452
bool isSimilarity(SkScalar tol=SK_ScalarNearlyZero) const
Definition SkMatrix.cpp:180
static constexpr int kMSkewX
horizontal skew factor
Definition SkMatrix.h:354
bool mapRect(SkRect *dst, const SkRect &src, SkApplyPerspectiveClip pc=SkApplyPerspectiveClip::kYes) const
static bool EqualRadii(const SkRRect &rr)
Definition SkRRectPriv.h:35
static bool IsSimpleCircular(const SkRRect &rr)
Definition SkRRectPriv.h:27
static SkVector GetSimpleRadii(const SkRRect &rr)
Definition SkRRectPriv.h:22
bool isOval() const
Definition SkRRect.h:85
static SkRRect MakeOval(const SkRect &oval)
Definition SkRRect.h:162
const SkRect & getBounds() const
Definition SkRRect.h:279
void void void appendf(const char format[],...) SK_PRINTF_LIKE(2
Definition SkString.cpp:550
static Domain GenerateDomain()
const Paint & paint
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition main.cc:19
float SkScalar
Definition extension.cpp:12
struct MyStruct s
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
uint32_t * target
Optional< SkRect > bounds
Definition SkRecords.h:189
SkRRect rrect
Definition SkRecords.h:232
static GrSurfaceProxyView create_falloff_texture(GrRecordingContext *rContext)
GrOp::Owner Make(GrRecordingContext *context, GrColor color, const SkMatrix &viewMatrix, const SkRRect &rrect, SkScalar blurWidth, SkScalar insetWidth)
Mipmapped
Definition GpuTypes.h:53
Definition ref_ptr.h:256
static SkRect inset(const SkRect &r)
const Scalar scale
void setIndexed(sk_sp< const GrBuffer > indexBuffer, int indexCount, int baseIndex, uint16_t minIndexValue, uint16_t maxIndexValue, GrPrimitiveRestart, sk_sp< const GrBuffer > vertexBuffer, int baseVertex)
static const GrUserStencilSettings & kUnused
static SkImageInfo MakeA8(int width, int height)
float fX
x-axis value
static constexpr SkPoint Make(float x, float y)
bool normalize()
Definition SkPoint.cpp:22
constexpr float height() const
Definition SkRect.h:769
constexpr float width() const
Definition SkRect.h:762
constexpr size_t kWidth