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/private/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#ifdef SK_DEBUG
243 SkString dumpInfo() const override {
244 SkString string;
245 for (int i = 0; i < fGeoData.count(); ++i) {
246 string.appendf(
247 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
248 "OuterRad: %.2f, Umbra: %.2f, InnerRad: %.2f, BlurRad: %.2f\n",
249 fGeoData[i].fColor, fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
250 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
251 fGeoData[i].fOuterRadius, fGeoData[i].fUmbraInset,
252 fGeoData[i].fInnerRadius, fGeoData[i].fBlurRadius);
253 }
254 string.append(INHERITED::dumpInfo());
255 return string;
256 }
257#endif
258
259 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
260
261 GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*,
262 bool hasMixedSampledCoverage, GrClampType) override {
263 return GrProcessorSet::EmptySetAnalysis();
264 }
265
266private:
267 struct Geometry {
268 GrColor fColor;
269 SkScalar fOuterRadius;
270 SkScalar fUmbraInset;
271 SkScalar fInnerRadius;
272 SkScalar fBlurRadius;
273 SkRect fDevBounds;
274 RRectType fType;
275 bool fIsCircle;
276 };
277
278 struct CircleVertex {
279 SkPoint fPos;
280 GrColor fColor;
281 SkPoint fOffset;
282 SkScalar fDistanceCorrection;
283 };
284
285 void fillInCircleVerts(const Geometry& args, bool isStroked, CircleVertex** verts) const {
286
287 GrColor color = args.fColor;
288 SkScalar outerRadius = args.fOuterRadius;
289 SkScalar innerRadius = args.fInnerRadius;
290 SkScalar blurRadius = args.fBlurRadius;
291 SkScalar distanceCorrection = outerRadius / blurRadius;
292
293 const SkRect& bounds = args.fDevBounds;
294
295 // The inner radius in the vertex data must be specified in normalized space.
296 innerRadius = innerRadius / outerRadius;
297
298 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
299 SkScalar halfWidth = 0.5f * bounds.width();
300 SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1
301
302 (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth);
303 (*verts)->fColor = color;
304 (*verts)->fOffset = SkPoint::Make(-octOffset, -1);
305 (*verts)->fDistanceCorrection = distanceCorrection;
306 (*verts)++;
307
308 (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth);
309 (*verts)->fColor = color;
310 (*verts)->fOffset = SkPoint::Make(octOffset, -1);
311 (*verts)->fDistanceCorrection = distanceCorrection;
312 (*verts)++;
313
314 (*verts)->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth);
315 (*verts)->fColor = color;
316 (*verts)->fOffset = SkPoint::Make(1, -octOffset);
317 (*verts)->fDistanceCorrection = distanceCorrection;
318 (*verts)++;
319
320 (*verts)->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth);
321 (*verts)->fColor = color;
322 (*verts)->fOffset = SkPoint::Make(1, octOffset);
323 (*verts)->fDistanceCorrection = distanceCorrection;
324 (*verts)++;
325
326 (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth);
327 (*verts)->fColor = color;
328 (*verts)->fOffset = SkPoint::Make(octOffset, 1);
329 (*verts)->fDistanceCorrection = distanceCorrection;
330 (*verts)++;
331
332 (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth);
333 (*verts)->fColor = color;
334 (*verts)->fOffset = SkPoint::Make(-octOffset, 1);
335 (*verts)->fDistanceCorrection = distanceCorrection;
336 (*verts)++;
337
338 (*verts)->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth);
339 (*verts)->fColor = color;
340 (*verts)->fOffset = SkPoint::Make(-1, octOffset);
341 (*verts)->fDistanceCorrection = distanceCorrection;
342 (*verts)++;
343
344 (*verts)->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth);
345 (*verts)->fColor = color;
346 (*verts)->fOffset = SkPoint::Make(-1, -octOffset);
347 (*verts)->fDistanceCorrection = distanceCorrection;
348 (*verts)++;
349
350 if (isStroked) {
351 // compute the inner ring
352
353 // cosine and sine of pi/8
354 SkScalar c = 0.923579533f;
355 SkScalar s = 0.382683432f;
356 SkScalar r = args.fInnerRadius;
357
358 (*verts)->fPos = center + SkPoint::Make(-s * r, -c * r);
359 (*verts)->fColor = color;
360 (*verts)->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius);
361 (*verts)->fDistanceCorrection = distanceCorrection;
362 (*verts)++;
363
364 (*verts)->fPos = center + SkPoint::Make(s * r, -c * r);
365 (*verts)->fColor = color;
366 (*verts)->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius);
367 (*verts)->fDistanceCorrection = distanceCorrection;
368 (*verts)++;
369
370 (*verts)->fPos = center + SkPoint::Make(c * r, -s * r);
371 (*verts)->fColor = color;
372 (*verts)->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius);
373 (*verts)->fDistanceCorrection = distanceCorrection;
374 (*verts)++;
375
376 (*verts)->fPos = center + SkPoint::Make(c * r, s * r);
377 (*verts)->fColor = color;
378 (*verts)->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius);
379 (*verts)->fDistanceCorrection = distanceCorrection;
380 (*verts)++;
381
382 (*verts)->fPos = center + SkPoint::Make(s * r, c * r);
383 (*verts)->fColor = color;
384 (*verts)->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius);
385 (*verts)->fDistanceCorrection = distanceCorrection;
386 (*verts)++;
387
388 (*verts)->fPos = center + SkPoint::Make(-s * r, c * r);
389 (*verts)->fColor = color;
390 (*verts)->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius);
391 (*verts)->fDistanceCorrection = distanceCorrection;
392 (*verts)++;
393
394 (*verts)->fPos = center + SkPoint::Make(-c * r, s * r);
395 (*verts)->fColor = color;
396 (*verts)->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius);
397 (*verts)->fDistanceCorrection = distanceCorrection;
398 (*verts)++;
399
400 (*verts)->fPos = center + SkPoint::Make(-c * r, -s * r);
401 (*verts)->fColor = color;
402 (*verts)->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius);
403 (*verts)->fDistanceCorrection = distanceCorrection;
404 (*verts)++;
405 } else {
406 // filled
407 (*verts)->fPos = center;
408 (*verts)->fColor = color;
409 (*verts)->fOffset = SkPoint::Make(0, 0);
410 (*verts)->fDistanceCorrection = distanceCorrection;
411 (*verts)++;
412 }
413 }
414
415 void fillInRRectVerts(const Geometry& args, CircleVertex** verts) const {
416 GrColor color = args.fColor;
417 SkScalar outerRadius = args.fOuterRadius;
418
419 const SkRect& bounds = args.fDevBounds;
420
421 SkScalar umbraInset = args.fUmbraInset;
422 SkScalar minDim = 0.5f*std::min(bounds.width(), bounds.height());
423 if (umbraInset > minDim) {
424 umbraInset = minDim;
425 }
426
427 SkScalar xInner[4] = { bounds.fLeft + umbraInset, bounds.fRight - umbraInset,
428 bounds.fLeft + umbraInset, bounds.fRight - umbraInset };
429 SkScalar xMid[4] = { bounds.fLeft + outerRadius, bounds.fRight - outerRadius,
430 bounds.fLeft + outerRadius, bounds.fRight - outerRadius };
431 SkScalar xOuter[4] = { bounds.fLeft, bounds.fRight,
432 bounds.fLeft, bounds.fRight };
433 SkScalar yInner[4] = { bounds.fTop + umbraInset, bounds.fTop + umbraInset,
434 bounds.fBottom - umbraInset, bounds.fBottom - umbraInset };
435 SkScalar yMid[4] = { bounds.fTop + outerRadius, bounds.fTop + outerRadius,
436 bounds.fBottom - outerRadius, bounds.fBottom - outerRadius };
437 SkScalar yOuter[4] = { bounds.fTop, bounds.fTop,
438 bounds.fBottom, bounds.fBottom };
439
440 SkScalar blurRadius = args.fBlurRadius;
441
442 // In the case where we have to inset more for the umbra, our two triangles in the
443 // corner get skewed to a diamond rather than a square. To correct for that,
444 // we also skew the vectors we send to the shader that help define the circle.
445 // By doing so, we end up with a quarter circle in the corner rather than the
446 // elliptical curve.
447
448 // This is a bit magical, but it gives us the correct results at extrema:
449 // a) umbraInset == outerRadius produces an orthogonal vector
450 // b) outerRadius == 0 produces a diagonal vector
451 // And visually the corner looks correct.
452 SkVector outerVec = SkVector::Make(outerRadius - umbraInset, -outerRadius - umbraInset);
453 outerVec.normalize();
454 // We want the circle edge to fall fractionally along the diagonal at
455 // (sqrt(2)*(umbraInset - outerRadius) + outerRadius)/sqrt(2)*umbraInset
456 //
457 // Setting the components of the diagonal offset to the following value will give us that.
458 SkScalar diagVal = umbraInset / (SK_ScalarSqrt2*(outerRadius - umbraInset) - outerRadius);
459 SkVector diagVec = SkVector::Make(diagVal, diagVal);
460 SkScalar distanceCorrection = umbraInset / blurRadius;
461
462 // build corner by corner
463 for (int i = 0; i < 4; ++i) {
464 // inner point
465 (*verts)->fPos = SkPoint::Make(xInner[i], yInner[i]);
466 (*verts)->fColor = color;
467 (*verts)->fOffset = SkVector::Make(0, 0);
468 (*verts)->fDistanceCorrection = distanceCorrection;
469 (*verts)++;
470
471 // outer points
472 (*verts)->fPos = SkPoint::Make(xOuter[i], yInner[i]);
473 (*verts)->fColor = color;
474 (*verts)->fOffset = SkVector::Make(0, -1);
475 (*verts)->fDistanceCorrection = distanceCorrection;
476 (*verts)++;
477
478 (*verts)->fPos = SkPoint::Make(xOuter[i], yMid[i]);
479 (*verts)->fColor = color;
480 (*verts)->fOffset = outerVec;
481 (*verts)->fDistanceCorrection = distanceCorrection;
482 (*verts)++;
483
484 (*verts)->fPos = SkPoint::Make(xOuter[i], yOuter[i]);
485 (*verts)->fColor = color;
486 (*verts)->fOffset = diagVec;
487 (*verts)->fDistanceCorrection = distanceCorrection;
488 (*verts)++;
489
490 (*verts)->fPos = SkPoint::Make(xMid[i], yOuter[i]);
491 (*verts)->fColor = color;
492 (*verts)->fOffset = outerVec;
493 (*verts)->fDistanceCorrection = distanceCorrection;
494 (*verts)++;
495
496 (*verts)->fPos = SkPoint::Make(xInner[i], yOuter[i]);
497 (*verts)->fColor = color;
498 (*verts)->fOffset = SkVector::Make(0, -1);
499 (*verts)->fDistanceCorrection = distanceCorrection;
500 (*verts)++;
501 }
502
503 // Add the additional vertices for overstroked rrects.
504 // Effectively this is an additional stroked rrect, with its
505 // parameters equal to those in the center of the 9-patch. This will
506 // give constant values across this inner ring.
507 if (kOverstroke_RRectType == args.fType) {
508 SkASSERT(args.fInnerRadius > 0.0f);
509
510 SkScalar inset = umbraInset + args.fInnerRadius;
511
512 // TL
513 (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fTop + inset);
514 (*verts)->fColor = color;
515 (*verts)->fOffset = SkPoint::Make(0, 0);
516 (*verts)->fDistanceCorrection = distanceCorrection;
517 (*verts)++;
518
519 // TR
520 (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fTop + inset);
521 (*verts)->fColor = color;
522 (*verts)->fOffset = SkPoint::Make(0, 0);
523 (*verts)->fDistanceCorrection = distanceCorrection;
524 (*verts)++;
525
526 // BL
527 (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fBottom - inset);
528 (*verts)->fColor = color;
529 (*verts)->fOffset = SkPoint::Make(0, 0);
530 (*verts)->fDistanceCorrection = distanceCorrection;
531 (*verts)++;
532
533 // BR
534 (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fBottom - inset);
535 (*verts)->fColor = color;
536 (*verts)->fOffset = SkPoint::Make(0, 0);
537 (*verts)->fDistanceCorrection = distanceCorrection;
538 (*verts)++;
539 }
540
541 }
542
543 GrProgramInfo* programInfo() override { return fProgramInfo; }
544
545 void onCreateProgramInfo(const GrCaps* caps,
546 SkArenaAlloc* arena,
547 const GrSurfaceProxyView* writeView,
548 GrAppliedClip&& appliedClip,
549 const GrXferProcessor::DstProxyView& dstProxyView) override {
550 GrGeometryProcessor* gp = GrRRectShadowGeoProc::Make(arena, fFalloffView);
551 SkASSERT(sizeof(CircleVertex) == gp->vertexStride());
552
553 fProgramInfo = GrSimpleMeshDrawOpHelper::CreateProgramInfo(caps, arena, writeView,
554 std::move(appliedClip),
555 dstProxyView, gp,
556 GrProcessorSet::MakeEmptySet(),
557 GrPrimitiveType::kTriangles,
558 GrPipeline::InputFlags::kNone,
559 &GrUserStencilSettings::kUnused);
560 }
561
562 void onPrepareDraws(Target* target) override {
563 int instanceCount = fGeoData.count();
564
565 sk_sp<const GrBuffer> vertexBuffer;
566 int firstVertex;
567 CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(
568 sizeof(CircleVertex), fVertCount, &vertexBuffer, &firstVertex);
569 if (!verts) {
570 SkDebugf("Could not allocate vertices\n");
571 return;
572 }
573
574 sk_sp<const GrBuffer> indexBuffer;
575 int firstIndex = 0;
576 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
577 if (!indices) {
578 SkDebugf("Could not allocate indices\n");
579 return;
580 }
581
582 int currStartVertex = 0;
583 for (int i = 0; i < instanceCount; i++) {
584 const Geometry& args = fGeoData[i];
585
586 if (args.fIsCircle) {
587 bool isStroked = SkToBool(kStroke_RRectType == args.fType);
588 this->fillInCircleVerts(args, isStroked, &verts);
589
590 const uint16_t* primIndices = circle_type_to_indices(isStroked);
591 const int primIndexCount = circle_type_to_index_count(isStroked);
592 for (int i = 0; i < primIndexCount; ++i) {
593 *indices++ = primIndices[i] + currStartVertex;
594 }
595
596 currStartVertex += circle_type_to_vert_count(isStroked);
597
598 } else {
599 this->fillInRRectVerts(args, &verts);
600
601 const uint16_t* primIndices = rrect_type_to_indices(args.fType);
602 const int primIndexCount = rrect_type_to_index_count(args.fType);
603 for (int i = 0; i < primIndexCount; ++i) {
604 *indices++ = primIndices[i] + currStartVertex;
605 }
606
607 currStartVertex += rrect_type_to_vert_count(args.fType);
608 }
609 }
610
611 fMesh = target->allocMesh();
612 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
613 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
614 }
615
616 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
617 if (!fProgramInfo) {
618 this->createProgramInfo(flushState);
619 }
620
621 if (!fProgramInfo || !fMesh) {
622 return;
623 }
624
625 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
626 flushState->bindTextures(fProgramInfo->primProc(), *fFalloffView.proxy(),
627 fProgramInfo->pipeline());
628 flushState->drawMesh(*fMesh);
629 }
630
631 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
632 const GrCaps& caps) override {
633 ShadowCircularRRectOp* that = t->cast<ShadowCircularRRectOp>();
634 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
635 fVertCount += that->fVertCount;
636 fIndexCount += that->fIndexCount;
637 return CombineResult::kMerged;
638 }
639
640 void visitProxies(const VisitProxyFunc& func) const override {
641 func(fFalloffView.proxy(), GrMipMapped(false));
642 if (fProgramInfo) {
643 fProgramInfo->visitFPProxies(func);
644 }
645 }
646
647 SkSTArray<1, Geometry, true> fGeoData;
648 int fVertCount;
649 int fIndexCount;
650 GrSurfaceProxyView fFalloffView;
651
652 GrSimpleMesh* fMesh = nullptr;
653 GrProgramInfo* fProgramInfo = nullptr;
654
655 typedef GrMeshDrawOp INHERITED;
656};
657
658} // anonymous namespace
659
660///////////////////////////////////////////////////////////////////////////////
661
662namespace GrShadowRRectOp {
663
664static GrSurfaceProxyView create_falloff_texture(GrRecordingContext* context) {
665 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
666 GrUniqueKey key;
667 GrUniqueKey::Builder builder(&key, kDomain, 0, "Shadow Gaussian Falloff");
668 builder.finish();
669
670 GrProxyProvider* proxyProvider = context->priv().proxyProvider();
671
672 if (sk_sp<GrTextureProxy> falloffTexture = proxyProvider->findOrCreateProxyByUniqueKey(key)) {
673 GrSwizzle swizzle = context->priv().caps()->getReadSwizzle(falloffTexture->backendFormat(),
674 GrColorType::kAlpha_8);
675 return {std::move(falloffTexture), kTopLeft_GrSurfaceOrigin, swizzle};
676 }
677
678 static const int kWidth = 128;
679 static const size_t kRowBytes = kWidth * GrColorTypeBytesPerPixel(GrColorType::kAlpha_8);
680 SkImageInfo ii = SkImageInfo::MakeA8(kWidth, 1);
681
682 SkBitmap bitmap;
683 bitmap.allocPixels(ii, kRowBytes);
684
685 unsigned char* values = (unsigned char*)bitmap.getPixels();
686 for (int i = 0; i < 128; ++i) {
687 SkScalar d = SK_Scalar1 - i / SkIntToScalar(127);
688 values[i] = SkScalarRoundToInt((SkScalarExp(-4 * d * d) - 0.018f) * 255);
689 }
690 bitmap.setImmutable();
691
692 GrBitmapTextureMaker maker(context, bitmap, GrImageTexGenPolicy::kNew_Uncached_Budgeted);
693 auto view = maker.view(GrMipMapped::kNo);
694 SkASSERT(view.origin() == kTopLeft_GrSurfaceOrigin);
695
696 if (view) {
697 proxyProvider->assignUniqueKeyToProxy(key, view.asTextureProxy());
698 }
699 return view;
700}
701
702
703std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
704 GrColor color,
705 const SkMatrix& viewMatrix,
706 const SkRRect& rrect,
707 SkScalar blurWidth,
708 SkScalar insetWidth) {
709 // Shadow rrect ops only handle simple circular rrects.
710 SkASSERT(viewMatrix.isSimilarity() && SkRRectPriv::EqualRadii(rrect));
711
712 GrSurfaceProxyView falloffView = create_falloff_texture(context);
713 if (!falloffView) {
714 return nullptr;
715 }
716
717 // Do any matrix crunching before we reset the draw state for device coords.
718 const SkRect& rrectBounds = rrect.getBounds();
719 SkRect bounds;
720 viewMatrix.mapRect(&bounds, rrectBounds);
721
722 // Map radius and inset. As the matrix is a similarity matrix, this should be isotropic.
723 SkScalar radius = SkRRectPriv::GetSimpleRadii(rrect).fX;
724 SkScalar matrixFactor = viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewX];
725 SkScalar scaledRadius = SkScalarAbs(radius*matrixFactor);
726 SkScalar scaledInsetWidth = SkScalarAbs(insetWidth*matrixFactor);
727
728 if (scaledInsetWidth <= 0) {
729 return nullptr;
730 }
731
732 GrOpMemoryPool* pool = context->priv().opMemoryPool();
733
734 return pool->allocate<ShadowCircularRRectOp>(color, bounds,
735 scaledRadius,
736 rrect.isOval(),
737 blurWidth,
738 scaledInsetWidth,
739 std::move(falloffView));
740}
741}
742
743///////////////////////////////////////////////////////////////////////////////
744
745#if GR_TEST_UTILS
746
747GR_DRAW_OP_TEST_DEFINE(ShadowRRectOp) {
748 // We may choose matrix and inset values that cause the factory to fail. We loop until we find
749 // an acceptable combination.
750 do {
751 // create a similarity matrix
752 SkScalar rotate = random->nextSScalar1() * 360.f;
753 SkScalar translateX = random->nextSScalar1() * 1000.f;
754 SkScalar translateY = random->nextSScalar1() * 1000.f;
755 SkScalar scale = random->nextSScalar1() * 100.f;
756 SkMatrix viewMatrix;
757 viewMatrix.setRotate(rotate);
758 viewMatrix.postTranslate(translateX, translateY);
759 viewMatrix.postScale(scale, scale);
760 SkScalar insetWidth = random->nextSScalar1() * 72.f;
761 SkScalar blurWidth = random->nextSScalar1() * 72.f;
762 bool isCircle = random->nextBool();
763 // This op doesn't use a full GrPaint, just a color.
764 GrColor color = paint.getColor4f().toBytes_RGBA();
765 if (isCircle) {
766 SkRect circle = GrTest::TestSquare(random);
767 SkRRect rrect = SkRRect::MakeOval(circle);
768 if (auto op = GrShadowRRectOp::Make(
769 context, color, viewMatrix, rrect, blurWidth, insetWidth)) {
770 return op;
771 }
772 } else {
773 SkRRect rrect;
774 do {
775 // This may return a rrect with elliptical corners, which will cause an assert.
776 rrect = GrTest::TestRRectSimple(random);
777 } while (!SkRRectPriv::IsSimpleCircular(rrect));
778 if (auto op = GrShadowRRectOp::Make(
779 context, color, viewMatrix, rrect, blurWidth, insetWidth)) {
780 return op;
781 }
782 }
783 } while (true);
784}
785
786#endif
787