1/*
2 * Copyright 2016 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "src/gpu/ops/GrShadowRRectOp.h"
9
10#include "include/gpu/GrRecordingContext.h"
11#include "src/core/SkRRectPriv.h"
12#include "src/gpu/GrBitmapTextureMaker.h"
13#include "src/gpu/GrDrawOpTest.h"
14#include "src/gpu/GrMemoryPool.h"
15#include "src/gpu/GrOpFlushState.h"
16#include "src/gpu/GrProgramInfo.h"
17#include "src/gpu/GrProxyProvider.h"
18#include "src/gpu/GrRecordingContextPriv.h"
19#include "src/gpu/effects/GrShadowGeoProc.h"
20#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
21
22///////////////////////////////////////////////////////////////////////////////
23// Circle Data
24//
25// We have two possible cases for geometry for a circle:
26
27// In the case of a normal fill, we draw geometry for the circle as an octagon.
28static const uint16_t gFillCircleIndices[] = {
29 // enter the octagon
30 // clang-format off
31 0, 1, 8, 1, 2, 8,
32 2, 3, 8, 3, 4, 8,
33 4, 5, 8, 5, 6, 8,
34 6, 7, 8, 7, 0, 8,
35 // clang-format on
36};
37
38// For stroked circles, we use two nested octagons.
39static const uint16_t gStrokeCircleIndices[] = {
40 // enter the octagon
41 // clang-format off
42 0, 1, 9, 0, 9, 8,
43 1, 2, 10, 1, 10, 9,
44 2, 3, 11, 2, 11, 10,
45 3, 4, 12, 3, 12, 11,
46 4, 5, 13, 4, 13, 12,
47 5, 6, 14, 5, 14, 13,
48 6, 7, 15, 6, 15, 14,
49 7, 0, 8, 7, 8, 15,
50 // clang-format on
51};
52
53static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
54static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
55static const int kVertsPerStrokeCircle = 16;
56static const int kVertsPerFillCircle = 9;
57
58static int circle_type_to_vert_count(bool stroked) {
59 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
60}
61
62static int circle_type_to_index_count(bool stroked) {
63 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
64}
65
66static const uint16_t* circle_type_to_indices(bool stroked) {
67 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
68}
69
70///////////////////////////////////////////////////////////////////////////////
71// RoundRect Data
72//
73// The geometry for a shadow roundrect is similar to a 9-patch:
74// ____________
75// |_|________|_|
76// | | | |
77// | | | |
78// | | | |
79// |_|________|_|
80// |_|________|_|
81//
82// However, each corner is rendered as a fan rather than a simple quad, as below. (The diagram
83// shows the upper part of the upper left corner. The bottom triangle would similarly be split
84// into two triangles.)
85// ________
86// |\ \ |
87// | \ \ |
88// | \\ |
89// | \|
90// --------
91//
92// The center of the fan handles the curve of the corner. For roundrects where the stroke width
93// is greater than the corner radius, the outer triangles blend from the curve to the straight
94// sides. Otherwise these triangles will be degenerate.
95//
96// In the case where the stroke width is greater than the corner radius and the
97// blur radius (overstroke), we add additional geometry to mark out the rectangle in the center.
98// This rectangle extends the coverage values of the center edges of the 9-patch.
99// ____________
100// |_|________|_|
101// | |\ ____ /| |
102// | | | | | |
103// | | |____| | |
104// |_|/______\|_|
105// |_|________|_|
106//
107// For filled rrects we reuse the stroke geometry but add an additional quad to the center.
108
109static const uint16_t gRRectIndices[] = {
110 // clang-format off
111 // overstroke quads
112 // we place this at the beginning so that we can skip these indices when rendering as filled
113 0, 6, 25, 0, 25, 24,
114 6, 18, 27, 6, 27, 25,
115 18, 12, 26, 18, 26, 27,
116 12, 0, 24, 12, 24, 26,
117
118 // corners
119 0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5,
120 6, 11, 10, 6, 10, 9, 6, 9, 8, 6, 8, 7,
121 12, 17, 16, 12, 16, 15, 12, 15, 14, 12, 14, 13,
122 18, 19, 20, 18, 20, 21, 18, 21, 22, 18, 22, 23,
123
124 // edges
125 0, 5, 11, 0, 11, 6,
126 6, 7, 19, 6, 19, 18,
127 18, 23, 17, 18, 17, 12,
128 12, 13, 1, 12, 1, 0,
129
130 // fill quad
131 // we place this at the end so that we can skip these indices when rendering as stroked
132 0, 6, 18, 0, 18, 12,
133 // clang-format on
134};
135
136// overstroke count
137static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
138// simple stroke count skips overstroke indices
139static const int kIndicesPerStrokeRRect = kIndicesPerOverstrokeRRect - 6*4;
140// fill count adds final quad to stroke count
141static const int kIndicesPerFillRRect = kIndicesPerStrokeRRect + 6;
142static const int kVertsPerStrokeRRect = 24;
143static const int kVertsPerOverstrokeRRect = 28;
144static const int kVertsPerFillRRect = 24;
145
146enum RRectType {
147 kFill_RRectType,
148 kStroke_RRectType,
149 kOverstroke_RRectType,
150};
151
152static int rrect_type_to_vert_count(RRectType type) {
153 switch (type) {
154 case kFill_RRectType:
155 return kVertsPerFillRRect;
156 case kStroke_RRectType:
157 return kVertsPerStrokeRRect;
158 case kOverstroke_RRectType:
159 return kVertsPerOverstrokeRRect;
160 }
161 SK_ABORT("Invalid type");
162}
163
164static int rrect_type_to_index_count(RRectType type) {
165 switch (type) {
166 case kFill_RRectType:
167 return kIndicesPerFillRRect;
168 case kStroke_RRectType:
169 return kIndicesPerStrokeRRect;
170 case kOverstroke_RRectType:
171 return kIndicesPerOverstrokeRRect;
172 }
173 SK_ABORT("Invalid type");
174}
175
176static const uint16_t* rrect_type_to_indices(RRectType type) {
177 switch (type) {
178 case kFill_RRectType:
179 case kStroke_RRectType:
180 return gRRectIndices + 6*4;
181 case kOverstroke_RRectType:
182 return gRRectIndices;
183 }
184 SK_ABORT("Invalid type");
185}
186
187///////////////////////////////////////////////////////////////////////////////
188namespace {
189
190class ShadowCircularRRectOp final : public GrMeshDrawOp {
191public:
192 DEFINE_OP_CLASS_ID
193
194 // An insetWidth > 1/2 rect width or height indicates a simple fill.
195 ShadowCircularRRectOp(GrColor color, const SkRect& devRect,
196 float devRadius, bool isCircle, float blurRadius, float insetWidth,
197 GrSurfaceProxyView falloffView)
198 : INHERITED(ClassID())
199 , fFalloffView(std::move(falloffView)) {
200 SkRect bounds = devRect;
201 SkASSERT(insetWidth > 0);
202 SkScalar innerRadius = 0.0f;
203 SkScalar outerRadius = devRadius;
204 SkScalar umbraInset;
205
206 RRectType type = kFill_RRectType;
207 if (isCircle) {
208 umbraInset = 0;
209 } else {
210 umbraInset = std::max(outerRadius, blurRadius);
211 }
212
213 // If stroke is greater than width or height, this is still a fill,
214 // otherwise we compute stroke params.
215 if (isCircle) {
216 innerRadius = devRadius - insetWidth;
217 type = innerRadius > 0 ? kStroke_RRectType : kFill_RRectType;
218 } else {
219 if (insetWidth <= 0.5f*std::min(devRect.width(), devRect.height())) {
220 // We don't worry about a real inner radius, we just need to know if we
221 // need to create overstroke vertices.
222 innerRadius = std::max(insetWidth - umbraInset, 0.0f);
223 type = innerRadius > 0 ? kOverstroke_RRectType : kStroke_RRectType;
224 }
225 }
226
227 this->setBounds(bounds, HasAABloat::kNo, IsHairline::kNo);
228
229 fGeoData.emplace_back(Geometry{color, outerRadius, umbraInset, innerRadius,
230 blurRadius, bounds, type, isCircle});
231 if (isCircle) {
232 fVertCount = circle_type_to_vert_count(kStroke_RRectType == type);
233 fIndexCount = circle_type_to_index_count(kStroke_RRectType == type);
234 } else {
235 fVertCount = rrect_type_to_vert_count(type);
236 fIndexCount = rrect_type_to_index_count(type);
237 }
238 }
239
240 const char* name() const override { return "ShadowCircularRRectOp"; }
241
242 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
243
244 GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*,
245 bool hasMixedSampledCoverage, GrClampType) override {
246 return GrProcessorSet::EmptySetAnalysis();
247 }
248
249private:
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
268 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
398 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
526 GrProgramInfo* programInfo() override { return fProgramInfo; }
527
528 void onCreateProgramInfo(const GrCaps* caps,
529 SkArenaAlloc* arena,
530 const GrSurfaceProxyView* writeView,
531 GrAppliedClip&& appliedClip,
532 const GrXferProcessor::DstProxyView& dstProxyView) override {
533 GrGeometryProcessor* gp = GrRRectShadowGeoProc::Make(arena, fFalloffView);
534 SkASSERT(sizeof(CircleVertex) == gp->vertexStride());
535
536 fProgramInfo = GrSimpleMeshDrawOpHelper::CreateProgramInfo(caps, arena, writeView,
537 std::move(appliedClip),
538 dstProxyView, gp,
539 GrProcessorSet::MakeEmptySet(),
540 GrPrimitiveType::kTriangles,
541 GrPipeline::InputFlags::kNone,
542 &GrUserStencilSettings::kUnused);
543 }
544
545 void onPrepareDraws(Target* target) override {
546 int instanceCount = fGeoData.count();
547
548 sk_sp<const GrBuffer> vertexBuffer;
549 int firstVertex;
550 CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(
551 sizeof(CircleVertex), fVertCount, &vertexBuffer, &firstVertex);
552 if (!verts) {
553 SkDebugf("Could not allocate vertices\n");
554 return;
555 }
556
557 sk_sp<const GrBuffer> indexBuffer;
558 int firstIndex = 0;
559 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
560 if (!indices) {
561 SkDebugf("Could not allocate indices\n");
562 return;
563 }
564
565 int currStartVertex = 0;
566 for (int i = 0; i < instanceCount; i++) {
567 const Geometry& args = fGeoData[i];
568
569 if (args.fIsCircle) {
570 bool isStroked = SkToBool(kStroke_RRectType == args.fType);
571 this->fillInCircleVerts(args, isStroked, &verts);
572
573 const uint16_t* primIndices = circle_type_to_indices(isStroked);
574 const int primIndexCount = circle_type_to_index_count(isStroked);
575 for (int i = 0; i < primIndexCount; ++i) {
576 *indices++ = primIndices[i] + currStartVertex;
577 }
578
579 currStartVertex += circle_type_to_vert_count(isStroked);
580
581 } else {
582 this->fillInRRectVerts(args, &verts);
583
584 const uint16_t* primIndices = rrect_type_to_indices(args.fType);
585 const int primIndexCount = rrect_type_to_index_count(args.fType);
586 for (int i = 0; i < primIndexCount; ++i) {
587 *indices++ = primIndices[i] + currStartVertex;
588 }
589
590 currStartVertex += rrect_type_to_vert_count(args.fType);
591 }
592 }
593
594 fMesh = target->allocMesh();
595 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
596 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
597 }
598
599 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
600 if (!fProgramInfo) {
601 this->createProgramInfo(flushState);
602 }
603
604 if (!fProgramInfo || !fMesh) {
605 return;
606 }
607
608 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
609 flushState->bindTextures(fProgramInfo->primProc(), *fFalloffView.proxy(),
610 fProgramInfo->pipeline());
611 flushState->drawMesh(*fMesh);
612 }
613
614 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
615 const GrCaps& caps) override {
616 ShadowCircularRRectOp* that = t->cast<ShadowCircularRRectOp>();
617 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
618 fVertCount += that->fVertCount;
619 fIndexCount += that->fIndexCount;
620 return CombineResult::kMerged;
621 }
622
623#if GR_TEST_UTILS
624 SkString onDumpInfo() const override {
625 SkString string;
626 for (int i = 0; i < fGeoData.count(); ++i) {
627 string.appendf(
628 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
629 "OuterRad: %.2f, Umbra: %.2f, InnerRad: %.2f, BlurRad: %.2f\n",
630 fGeoData[i].fColor, fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
631 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
632 fGeoData[i].fOuterRadius, fGeoData[i].fUmbraInset,
633 fGeoData[i].fInnerRadius, fGeoData[i].fBlurRadius);
634 }
635 return string;
636 }
637#endif
638
639 void visitProxies(const VisitProxyFunc& func) const override {
640 func(fFalloffView.proxy(), GrMipmapped(false));
641 if (fProgramInfo) {
642 fProgramInfo->visitFPProxies(func);
643 }
644 }
645
646 SkSTArray<1, Geometry, true> fGeoData;
647 int fVertCount;
648 int fIndexCount;
649 GrSurfaceProxyView fFalloffView;
650
651 GrSimpleMesh* fMesh = nullptr;
652 GrProgramInfo* fProgramInfo = nullptr;
653
654 typedef GrMeshDrawOp INHERITED;
655};
656
657} // anonymous namespace
658
659///////////////////////////////////////////////////////////////////////////////
660
661namespace GrShadowRRectOp {
662
663static GrSurfaceProxyView create_falloff_texture(GrRecordingContext* context) {
664 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
665 GrUniqueKey key;
666 GrUniqueKey::Builder builder(&key, kDomain, 0, "Shadow Gaussian Falloff");
667 builder.finish();
668
669 GrProxyProvider* proxyProvider = context->priv().proxyProvider();
670
671 if (sk_sp<GrTextureProxy> falloffTexture = proxyProvider->findOrCreateProxyByUniqueKey(key)) {
672 GrSwizzle swizzle = context->priv().caps()->getReadSwizzle(falloffTexture->backendFormat(),
673 GrColorType::kAlpha_8);
674 return {std::move(falloffTexture), kTopLeft_GrSurfaceOrigin, swizzle};
675 }
676
677 static const int kWidth = 128;
678 static const size_t kRowBytes = kWidth * GrColorTypeBytesPerPixel(GrColorType::kAlpha_8);
679 SkImageInfo ii = SkImageInfo::MakeA8(kWidth, 1);
680
681 SkBitmap bitmap;
682 bitmap.allocPixels(ii, kRowBytes);
683
684 unsigned char* values = (unsigned char*)bitmap.getPixels();
685 for (int i = 0; i < 128; ++i) {
686 SkScalar d = SK_Scalar1 - i / SkIntToScalar(127);
687 values[i] = SkScalarRoundToInt((SkScalarExp(-4 * d * d) - 0.018f) * 255);
688 }
689 bitmap.setImmutable();
690
691 GrBitmapTextureMaker maker(context, bitmap, GrImageTexGenPolicy::kNew_Uncached_Budgeted);
692 auto view = maker.view(GrMipmapped::kNo);
693 SkASSERT(view.origin() == kTopLeft_GrSurfaceOrigin);
694
695 if (view) {
696 proxyProvider->assignUniqueKeyToProxy(key, view.asTextureProxy());
697 }
698 return view;
699}
700
701
702std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
703 GrColor color,
704 const SkMatrix& viewMatrix,
705 const SkRRect& rrect,
706 SkScalar blurWidth,
707 SkScalar insetWidth) {
708 // Shadow rrect ops only handle simple circular rrects.
709 SkASSERT(viewMatrix.isSimilarity() && SkRRectPriv::EqualRadii(rrect));
710
711 GrSurfaceProxyView falloffView = create_falloff_texture(context);
712 if (!falloffView) {
713 return nullptr;
714 }
715
716 // Do any matrix crunching before we reset the draw state for device coords.
717 const SkRect& rrectBounds = rrect.getBounds();
718 SkRect bounds;
719 viewMatrix.mapRect(&bounds, rrectBounds);
720
721 // Map radius and inset. As the matrix is a similarity matrix, this should be isotropic.
722 SkScalar radius = SkRRectPriv::GetSimpleRadii(rrect).fX;
723 SkScalar matrixFactor = viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewX];
724 SkScalar scaledRadius = SkScalarAbs(radius*matrixFactor);
725 SkScalar scaledInsetWidth = SkScalarAbs(insetWidth*matrixFactor);
726
727 if (scaledInsetWidth <= 0) {
728 return nullptr;
729 }
730
731 GrOpMemoryPool* pool = context->priv().opMemoryPool();
732
733 return pool->allocate<ShadowCircularRRectOp>(color, bounds,
734 scaledRadius,
735 rrect.isOval(),
736 blurWidth,
737 scaledInsetWidth,
738 std::move(falloffView));
739}
740} // namespace GrShadowRRectOp
741
742///////////////////////////////////////////////////////////////////////////////
743
744#if GR_TEST_UTILS
745
746GR_DRAW_OP_TEST_DEFINE(ShadowRRectOp) {
747 // We may choose matrix and inset values that cause the factory to fail. We loop until we find
748 // an acceptable combination.
749 do {
750 // create a similarity matrix
751 SkScalar rotate = random->nextSScalar1() * 360.f;
752 SkScalar translateX = random->nextSScalar1() * 1000.f;
753 SkScalar translateY = random->nextSScalar1() * 1000.f;
754 SkScalar scale = random->nextSScalar1() * 100.f;
755 SkMatrix viewMatrix;
756 viewMatrix.setRotate(rotate);
757 viewMatrix.postTranslate(translateX, translateY);
758 viewMatrix.postScale(scale, scale);
759 SkScalar insetWidth = random->nextSScalar1() * 72.f;
760 SkScalar blurWidth = random->nextSScalar1() * 72.f;
761 bool isCircle = random->nextBool();
762 // This op doesn't use a full GrPaint, just a color.
763 GrColor color = paint.getColor4f().toBytes_RGBA();
764 if (isCircle) {
765 SkRect circle = GrTest::TestSquare(random);
766 SkRRect rrect = SkRRect::MakeOval(circle);
767 if (auto op = GrShadowRRectOp::Make(
768 context, color, viewMatrix, rrect, blurWidth, insetWidth)) {
769 return op;
770 }
771 } else {
772 SkRRect rrect;
773 do {
774 // This may return a rrect with elliptical corners, which will cause an assert.
775 rrect = GrTest::TestRRectSimple(random);
776 } while (!SkRRectPriv::IsSimpleCircular(rrect));
777 if (auto op = GrShadowRRectOp::Make(
778 context, color, viewMatrix, rrect, blurWidth, insetWidth)) {
779 return op;
780 }
781 }
782 } while (true);
783}
784
785#endif
786