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 "GrShadowRRectOp.h"
9 #include "GrDrawOpTest.h"
10 #include "GrOpFlushState.h"
11 #include "SkRRectPriv.h"
12 #include "effects/GrShadowGeoProc.h"
13
14 ///////////////////////////////////////////////////////////////////////////////
15 // Circle Data
16 //
17 // We have two possible cases for geometry for a circle:
18
19 // In the case of a normal fill, we draw geometry for the circle as an octagon.
20 static const uint16_t gFillCircleIndices[] = {
21 // enter the octagon
22 // clang-format off
23 0, 1, 8, 1, 2, 8,
24 2, 3, 8, 3, 4, 8,
25 4, 5, 8, 5, 6, 8,
26 6, 7, 8, 7, 0, 8,
27 // clang-format on
28 };
29
30 // For stroked circles, we use two nested octagons.
31 static const uint16_t gStrokeCircleIndices[] = {
32 // enter the octagon
33 // clang-format off
34 0, 1, 9, 0, 9, 8,
35 1, 2, 10, 1, 10, 9,
36 2, 3, 11, 2, 11, 10,
37 3, 4, 12, 3, 12, 11,
38 4, 5, 13, 4, 13, 12,
39 5, 6, 14, 5, 14, 13,
40 6, 7, 15, 6, 15, 14,
41 7, 0, 8, 7, 8, 15,
42 // clang-format on
43 };
44
45 static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
46 static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
47 static const int kVertsPerStrokeCircle = 16;
48 static const int kVertsPerFillCircle = 9;
49
circle_type_to_vert_count(bool stroked)50 static int circle_type_to_vert_count(bool stroked) {
51 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
52 }
53
circle_type_to_index_count(bool stroked)54 static int circle_type_to_index_count(bool stroked) {
55 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
56 }
57
circle_type_to_indices(bool stroked)58 static const uint16_t* circle_type_to_indices(bool stroked) {
59 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
60 }
61
62 ///////////////////////////////////////////////////////////////////////////////
63 // RoundRect Data
64 //
65 // The geometry for a shadow roundrect is similar to a 9-patch:
66 // ____________
67 // |_|________|_|
68 // | | | |
69 // | | | |
70 // | | | |
71 // |_|________|_|
72 // |_|________|_|
73 //
74 // However, each corner is rendered as a fan rather than a simple quad, as below. (The diagram
75 // shows the upper part of the upper left corner. The bottom triangle would similarly be split
76 // into two triangles.)
77 // ________
78 // |\ \ |
79 // | \ \ |
80 // | \\ |
81 // | \|
82 // --------
83 //
84 // The center of the fan handles the curve of the corner. For roundrects where the stroke width
85 // is greater than the corner radius, the outer triangles blend from the curve to the straight
86 // sides. Otherwise these triangles will be degenerate.
87 //
88 // In the case where the stroke width is greater than the corner radius and the
89 // blur radius (overstroke), we add additional geometry to mark out the rectangle in the center.
90 // This rectangle extends the coverage values of the center edges of the 9-patch.
91 // ____________
92 // |_|________|_|
93 // | |\ ____ /| |
94 // | | | | | |
95 // | | |____| | |
96 // |_|/______\|_|
97 // |_|________|_|
98 //
99 // For filled rrects we reuse the stroke geometry but add an additional quad to the center.
100
101 static const uint16_t gRRectIndices[] = {
102 // clang-format off
103 // overstroke quads
104 // we place this at the beginning so that we can skip these indices when rendering as filled
105 0, 6, 25, 0, 25, 24,
106 6, 18, 27, 6, 27, 25,
107 18, 12, 26, 18, 26, 27,
108 12, 0, 24, 12, 24, 26,
109
110 // corners
111 0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5,
112 6, 11, 10, 6, 10, 9, 6, 9, 8, 6, 8, 7,
113 12, 17, 16, 12, 16, 15, 12, 15, 14, 12, 14, 13,
114 18, 19, 20, 18, 20, 21, 18, 21, 22, 18, 22, 23,
115
116 // edges
117 0, 5, 11, 0, 11, 6,
118 6, 7, 19, 6, 19, 18,
119 18, 23, 17, 18, 17, 12,
120 12, 13, 1, 12, 1, 0,
121
122 // fill quad
123 // we place this at the end so that we can skip these indices when rendering as stroked
124 0, 6, 18, 0, 18, 12,
125 // clang-format on
126 };
127
128 // overstroke count
129 static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
130 // simple stroke count skips overstroke indices
131 static const int kIndicesPerStrokeRRect = kIndicesPerOverstrokeRRect - 6*4;
132 // fill count adds final quad to stroke count
133 static const int kIndicesPerFillRRect = kIndicesPerStrokeRRect + 6;
134 static const int kVertsPerStrokeRRect = 24;
135 static const int kVertsPerOverstrokeRRect = 28;
136 static const int kVertsPerFillRRect = 24;
137
138 enum RRectType {
139 kFill_RRectType,
140 kStroke_RRectType,
141 kOverstroke_RRectType,
142 };
143
rrect_type_to_vert_count(RRectType type)144 static int rrect_type_to_vert_count(RRectType type) {
145 switch (type) {
146 case kFill_RRectType:
147 return kVertsPerFillRRect;
148 case kStroke_RRectType:
149 return kVertsPerStrokeRRect;
150 case kOverstroke_RRectType:
151 return kVertsPerOverstrokeRRect;
152 }
153 SK_ABORT("Invalid type");
154 return 0;
155 }
156
rrect_type_to_index_count(RRectType type)157 static int rrect_type_to_index_count(RRectType type) {
158 switch (type) {
159 case kFill_RRectType:
160 return kIndicesPerFillRRect;
161 case kStroke_RRectType:
162 return kIndicesPerStrokeRRect;
163 case kOverstroke_RRectType:
164 return kIndicesPerOverstrokeRRect;
165 }
166 SK_ABORT("Invalid type");
167 return 0;
168 }
169
rrect_type_to_indices(RRectType type)170 static const uint16_t* rrect_type_to_indices(RRectType type) {
171 switch (type) {
172 case kFill_RRectType:
173 case kStroke_RRectType:
174 return gRRectIndices + 6*4;
175 case kOverstroke_RRectType:
176 return gRRectIndices;
177 }
178 SK_ABORT("Invalid type");
179 return nullptr;
180 }
181
182 ///////////////////////////////////////////////////////////////////////////////
183 namespace {
184
185 class ShadowCircularRRectOp final : public GrMeshDrawOp {
186 public:
187 DEFINE_OP_CLASS_ID
188
189 // 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,float blurClamp)190 ShadowCircularRRectOp(GrColor color, const SkRect& devRect,
191 float devRadius, bool isCircle, float blurRadius, float insetWidth,
192 float blurClamp)
193 : INHERITED(ClassID()) {
194 SkRect bounds = devRect;
195 SkASSERT(insetWidth > 0);
196 SkScalar innerRadius = 0.0f;
197 SkScalar outerRadius = devRadius;
198 SkScalar umbraInset;
199
200 RRectType type = kFill_RRectType;
201 if (isCircle) {
202 umbraInset = 0;
203 } else if (insetWidth > 0 && insetWidth <= outerRadius) {
204 // If the client has requested a stroke smaller than the outer radius,
205 // we will assume they want no special umbra inset (this is for ambient shadows).
206 umbraInset = outerRadius;
207 } else {
208 umbraInset = SkTMax(outerRadius, blurRadius);
209 }
210
211 // If stroke is greater than width or height, this is still a fill,
212 // otherwise we compute stroke params.
213 if (isCircle) {
214 innerRadius = devRadius - insetWidth;
215 type = innerRadius > 0 ? kStroke_RRectType : kFill_RRectType;
216 } else {
217 if (insetWidth <= 0.5f*SkTMin(devRect.width(), devRect.height())) {
218 // We don't worry about a real inner radius, we just need to know if we
219 // need to create overstroke vertices.
220 innerRadius = SkTMax(insetWidth - umbraInset, 0.0f);
221 type = innerRadius > 0 ? kOverstroke_RRectType : kStroke_RRectType;
222 }
223 }
224
225 this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
226
227 fGeoData.emplace_back(Geometry{color, outerRadius, umbraInset, innerRadius,
228 blurRadius, blurClamp, bounds, type, isCircle});
229 if (isCircle) {
230 fVertCount = circle_type_to_vert_count(kStroke_RRectType == type);
231 fIndexCount = circle_type_to_index_count(kStroke_RRectType == type);
232 } else {
233 fVertCount = rrect_type_to_vert_count(type);
234 fIndexCount = rrect_type_to_index_count(type);
235 }
236 }
237
name() const238 const char* name() const override { return "ShadowCircularRRectOp"; }
239
dumpInfo() const240 SkString dumpInfo() const override {
241 SkString string;
242 for (int i = 0; i < fGeoData.count(); ++i) {
243 string.appendf(
244 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
245 "OuterRad: %.2f, Umbra: %.2f, InnerRad: %.2f, BlurRad: %.2f\n",
246 fGeoData[i].fColor, fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
247 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
248 fGeoData[i].fOuterRadius, fGeoData[i].fUmbraInset,
249 fGeoData[i].fInnerRadius, fGeoData[i].fBlurRadius);
250 }
251 string.append(INHERITED::dumpInfo());
252 return string;
253 }
254
fixedFunctionFlags() const255 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
256
finalize(const GrCaps &,const GrAppliedClip *,GrPixelConfigIsClamped)257 RequiresDstTexture finalize(const GrCaps&, const GrAppliedClip*,
258 GrPixelConfigIsClamped) override {
259 return RequiresDstTexture::kNo;
260 }
261
262 private:
263 struct Geometry {
264 GrColor fColor;
265 SkScalar fOuterRadius;
266 SkScalar fUmbraInset;
267 SkScalar fInnerRadius;
268 SkScalar fBlurRadius;
269 SkScalar fClampValue;
270 SkRect fDevBounds;
271 RRectType fType;
272 bool fIsCircle;
273 };
274
275 struct CircleVertex {
276 SkPoint fPos;
277 GrColor fColor;
278 SkPoint fOffset;
279 SkScalar fDistanceCorrection;
280 SkScalar fClampValue;
281 };
282
fillInCircleVerts(const Geometry & args,bool isStroked,CircleVertex ** verts) const283 void fillInCircleVerts(const Geometry& args, bool isStroked, CircleVertex** verts) const {
284
285 GrColor color = args.fColor;
286 SkScalar outerRadius = args.fOuterRadius;
287 SkScalar innerRadius = args.fInnerRadius;
288 SkScalar blurRadius = args.fBlurRadius;
289 SkScalar distanceCorrection = outerRadius / blurRadius;
290 SkScalar clampValue = args.fClampValue;
291
292 const SkRect& bounds = args.fDevBounds;
293
294 // The inner radius in the vertex data must be specified in normalized space.
295 innerRadius = innerRadius / outerRadius;
296
297 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
298 SkScalar halfWidth = 0.5f * bounds.width();
299 SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1
300
301 (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth);
302 (*verts)->fColor = color;
303 (*verts)->fOffset = SkPoint::Make(-octOffset, -1);
304 (*verts)->fDistanceCorrection = distanceCorrection;
305 (*verts)->fClampValue = clampValue;
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)->fClampValue = clampValue;
313 (*verts)++;
314
315 (*verts)->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth);
316 (*verts)->fColor = color;
317 (*verts)->fOffset = SkPoint::Make(1, -octOffset);
318 (*verts)->fDistanceCorrection = distanceCorrection;
319 (*verts)->fClampValue = clampValue;
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)->fClampValue = clampValue;
327 (*verts)++;
328
329 (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth);
330 (*verts)->fColor = color;
331 (*verts)->fOffset = SkPoint::Make(octOffset, 1);
332 (*verts)->fDistanceCorrection = distanceCorrection;
333 (*verts)->fClampValue = clampValue;
334 (*verts)++;
335
336 (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth);
337 (*verts)->fColor = color;
338 (*verts)->fOffset = SkPoint::Make(-octOffset, 1);
339 (*verts)->fDistanceCorrection = distanceCorrection;
340 (*verts)->fClampValue = clampValue;
341 (*verts)++;
342
343 (*verts)->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth);
344 (*verts)->fColor = color;
345 (*verts)->fOffset = SkPoint::Make(-1, octOffset);
346 (*verts)->fDistanceCorrection = distanceCorrection;
347 (*verts)->fClampValue = clampValue;
348 (*verts)++;
349
350 (*verts)->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth);
351 (*verts)->fColor = color;
352 (*verts)->fOffset = SkPoint::Make(-1, -octOffset);
353 (*verts)->fDistanceCorrection = distanceCorrection;
354 (*verts)->fClampValue = clampValue;
355 (*verts)++;
356
357 if (isStroked) {
358 // compute the inner ring
359
360 // cosine and sine of pi/8
361 SkScalar c = 0.923579533f;
362 SkScalar s = 0.382683432f;
363 SkScalar r = args.fInnerRadius;
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)->fClampValue = clampValue;
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)->fClampValue = clampValue;
377 (*verts)++;
378
379 (*verts)->fPos = center + SkPoint::Make(c * r, -s * r);
380 (*verts)->fColor = color;
381 (*verts)->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius);
382 (*verts)->fDistanceCorrection = distanceCorrection;
383 (*verts)->fClampValue = clampValue;
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)->fClampValue = clampValue;
391 (*verts)++;
392
393 (*verts)->fPos = center + SkPoint::Make(s * r, c * r);
394 (*verts)->fColor = color;
395 (*verts)->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius);
396 (*verts)->fDistanceCorrection = distanceCorrection;
397 (*verts)->fClampValue = clampValue;
398 (*verts)++;
399
400 (*verts)->fPos = center + SkPoint::Make(-s * r, c * r);
401 (*verts)->fColor = color;
402 (*verts)->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius);
403 (*verts)->fDistanceCorrection = distanceCorrection;
404 (*verts)->fClampValue = clampValue;
405 (*verts)++;
406
407 (*verts)->fPos = center + SkPoint::Make(-c * r, s * r);
408 (*verts)->fColor = color;
409 (*verts)->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius);
410 (*verts)->fDistanceCorrection = distanceCorrection;
411 (*verts)->fClampValue = clampValue;
412 (*verts)++;
413
414 (*verts)->fPos = center + SkPoint::Make(-c * r, -s * r);
415 (*verts)->fColor = color;
416 (*verts)->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius);
417 (*verts)->fDistanceCorrection = distanceCorrection;
418 (*verts)->fClampValue = clampValue;
419 (*verts)++;
420 } else {
421 // filled
422 (*verts)->fPos = center;
423 (*verts)->fColor = color;
424 (*verts)->fOffset = SkPoint::Make(0, 0);
425 (*verts)->fDistanceCorrection = distanceCorrection;
426 (*verts)->fClampValue = clampValue;
427 (*verts)++;
428 }
429 }
430
fillInRRectVerts(const Geometry & args,CircleVertex ** verts) const431 void fillInRRectVerts(const Geometry& args, CircleVertex** verts) const {
432 GrColor color = args.fColor;
433 SkScalar outerRadius = args.fOuterRadius;
434
435 const SkRect& bounds = args.fDevBounds;
436
437 SkScalar umbraInset = args.fUmbraInset;
438 SkScalar minDim = 0.5f*SkTMin(bounds.width(), bounds.height());
439 if (umbraInset > minDim) {
440 umbraInset = minDim;
441 }
442
443 SkScalar xInner[4] = { bounds.fLeft + umbraInset, bounds.fRight - umbraInset,
444 bounds.fLeft + umbraInset, bounds.fRight - umbraInset };
445 SkScalar xMid[4] = { bounds.fLeft + outerRadius, bounds.fRight - outerRadius,
446 bounds.fLeft + outerRadius, bounds.fRight - outerRadius };
447 SkScalar xOuter[4] = { bounds.fLeft, bounds.fRight,
448 bounds.fLeft, bounds.fRight };
449 SkScalar yInner[4] = { bounds.fTop + umbraInset, bounds.fTop + umbraInset,
450 bounds.fBottom - umbraInset, bounds.fBottom - umbraInset };
451 SkScalar yMid[4] = { bounds.fTop + outerRadius, bounds.fTop + outerRadius,
452 bounds.fBottom - outerRadius, bounds.fBottom - outerRadius };
453 SkScalar yOuter[4] = { bounds.fTop, bounds.fTop,
454 bounds.fBottom, bounds.fBottom };
455
456 SkScalar blurRadius = args.fBlurRadius;
457
458 // In the case where we have to inset more for the umbra, our two triangles in the
459 // corner get skewed to a diamond rather than a square. To correct for that,
460 // we also skew the vectors we send to the shader that help define the circle.
461 // By doing so, we end up with a quarter circle in the corner rather than the
462 // elliptical curve.
463 SkVector outerVec = SkVector::Make(0.5f*(outerRadius - umbraInset), -umbraInset);
464 outerVec.normalize();
465 SkVector diagVec = SkVector::Make(outerVec.fX + outerVec.fY,
466 outerVec.fX + outerVec.fY);
467 diagVec *= umbraInset / (2 * umbraInset - outerRadius);
468 SkScalar distanceCorrection = umbraInset / blurRadius;
469 SkScalar clampValue = args.fClampValue;
470
471 // build corner by corner
472 for (int i = 0; i < 4; ++i) {
473 // inner point
474 (*verts)->fPos = SkPoint::Make(xInner[i], yInner[i]);
475 (*verts)->fColor = color;
476 (*verts)->fOffset = SkVector::Make(0, 0);
477 (*verts)->fDistanceCorrection = distanceCorrection;
478 (*verts)->fClampValue = clampValue;
479 (*verts)++;
480
481 // outer points
482 (*verts)->fPos = SkPoint::Make(xOuter[i], yInner[i]);
483 (*verts)->fColor = color;
484 (*verts)->fOffset = SkVector::Make(0, -1);
485 (*verts)->fDistanceCorrection = distanceCorrection;
486 (*verts)->fClampValue = clampValue;
487 (*verts)++;
488
489 (*verts)->fPos = SkPoint::Make(xOuter[i], yMid[i]);
490 (*verts)->fColor = color;
491 (*verts)->fOffset = outerVec;
492 (*verts)->fDistanceCorrection = distanceCorrection;
493 (*verts)->fClampValue = clampValue;
494 (*verts)++;
495
496 (*verts)->fPos = SkPoint::Make(xOuter[i], yOuter[i]);
497 (*verts)->fColor = color;
498 (*verts)->fOffset = diagVec;
499 (*verts)->fDistanceCorrection = distanceCorrection;
500 (*verts)->fClampValue = clampValue;
501 (*verts)++;
502
503 (*verts)->fPos = SkPoint::Make(xMid[i], yOuter[i]);
504 (*verts)->fColor = color;
505 (*verts)->fOffset = outerVec;
506 (*verts)->fDistanceCorrection = distanceCorrection;
507 (*verts)->fClampValue = clampValue;
508 (*verts)++;
509
510 (*verts)->fPos = SkPoint::Make(xInner[i], yOuter[i]);
511 (*verts)->fColor = color;
512 (*verts)->fOffset = SkVector::Make(0, -1);
513 (*verts)->fDistanceCorrection = distanceCorrection;
514 (*verts)->fClampValue = clampValue;
515 (*verts)++;
516 }
517
518 // Add the additional vertices for overstroked rrects.
519 // Effectively this is an additional stroked rrect, with its
520 // parameters equal to those in the center of the 9-patch. This will
521 // give constant values across this inner ring.
522 if (kOverstroke_RRectType == args.fType) {
523 SkASSERT(args.fInnerRadius > 0.0f);
524
525 SkScalar inset = umbraInset + args.fInnerRadius;
526
527 // TL
528 (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fTop + inset);
529 (*verts)->fColor = color;
530 (*verts)->fOffset = SkPoint::Make(0, 0);
531 (*verts)->fDistanceCorrection = distanceCorrection;
532 (*verts)->fClampValue = clampValue;
533 (*verts)++;
534
535 // TR
536 (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fTop + inset);
537 (*verts)->fColor = color;
538 (*verts)->fOffset = SkPoint::Make(0, 0);
539 (*verts)->fDistanceCorrection = distanceCorrection;
540 (*verts)->fClampValue = clampValue;
541 (*verts)++;
542
543 // BL
544 (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fBottom - inset);
545 (*verts)->fColor = color;
546 (*verts)->fOffset = SkPoint::Make(0, 0);
547 (*verts)->fDistanceCorrection = distanceCorrection;
548 (*verts)->fClampValue = clampValue;
549 (*verts)++;
550
551 // BR
552 (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fBottom - inset);
553 (*verts)->fColor = color;
554 (*verts)->fOffset = SkPoint::Make(0, 0);
555 (*verts)->fDistanceCorrection = distanceCorrection;
556 (*verts)->fClampValue = clampValue;
557 (*verts)++;
558 }
559
560 }
561
onPrepareDraws(Target * target)562 void onPrepareDraws(Target* target) override {
563 // Setup geometry processor
564 sk_sp<GrGeometryProcessor> gp = GrRRectShadowGeoProc::Make();
565
566 int instanceCount = fGeoData.count();
567 size_t vertexStride = gp->getVertexStride();
568 SkASSERT(sizeof(CircleVertex) == vertexStride);
569
570 const GrBuffer* vertexBuffer;
571 int firstVertex;
572 CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(vertexStride, fVertCount,
573 &vertexBuffer, &firstVertex);
574 if (!verts) {
575 SkDebugf("Could not allocate vertices\n");
576 return;
577 }
578
579 const GrBuffer* indexBuffer = nullptr;
580 int firstIndex = 0;
581 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
582 if (!indices) {
583 SkDebugf("Could not allocate indices\n");
584 return;
585 }
586
587 int currStartVertex = 0;
588 for (int i = 0; i < instanceCount; i++) {
589 const Geometry& args = fGeoData[i];
590
591 if (args.fIsCircle) {
592 bool isStroked = SkToBool(kStroke_RRectType == args.fType);
593 this->fillInCircleVerts(args, isStroked, &verts);
594
595 const uint16_t* primIndices = circle_type_to_indices(isStroked);
596 const int primIndexCount = circle_type_to_index_count(isStroked);
597 for (int i = 0; i < primIndexCount; ++i) {
598 *indices++ = primIndices[i] + currStartVertex;
599 }
600
601 currStartVertex += circle_type_to_vert_count(isStroked);
602
603 } else {
604 this->fillInRRectVerts(args, &verts);
605
606 const uint16_t* primIndices = rrect_type_to_indices(args.fType);
607 const int primIndexCount = rrect_type_to_index_count(args.fType);
608 for (int i = 0; i < primIndexCount; ++i) {
609 *indices++ = primIndices[i] + currStartVertex;
610 }
611
612 currStartVertex += rrect_type_to_vert_count(args.fType);
613 }
614 }
615
616 static const uint32_t kPipelineFlags = 0;
617 const GrPipeline* pipeline = target->makePipeline(
618 kPipelineFlags, GrProcessorSet::MakeEmptySet(), target->detachAppliedClip());
619
620 GrMesh mesh(GrPrimitiveType::kTriangles);
621 mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1);
622 mesh.setVertexData(vertexBuffer, firstVertex);
623 target->draw(gp.get(), pipeline, mesh);
624 }
625
onCombineIfPossible(GrOp * t,const GrCaps & caps)626 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
627 ShadowCircularRRectOp* that = t->cast<ShadowCircularRRectOp>();
628 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
629 this->joinBounds(*that);
630 fVertCount += that->fVertCount;
631 fIndexCount += that->fIndexCount;
632 return true;
633 }
634
635 SkSTArray<1, Geometry, true> fGeoData;
636 int fVertCount;
637 int fIndexCount;
638
639 typedef GrMeshDrawOp INHERITED;
640 };
641
642 } // anonymous namespace
643
644 ///////////////////////////////////////////////////////////////////////////////
645
646 namespace GrShadowRRectOp {
Make(GrColor color,const SkMatrix & viewMatrix,const SkRRect & rrect,SkScalar blurWidth,SkScalar insetWidth,SkScalar blurClamp)647 std::unique_ptr<GrDrawOp> Make(GrColor color,
648 const SkMatrix& viewMatrix,
649 const SkRRect& rrect,
650 SkScalar blurWidth,
651 SkScalar insetWidth,
652 SkScalar blurClamp) {
653 // Shadow rrect ops only handle simple circular rrects.
654 SkASSERT(viewMatrix.isSimilarity() && SkRRectPriv::EqualRadii(rrect));
655
656 // Do any matrix crunching before we reset the draw state for device coords.
657 const SkRect& rrectBounds = rrect.getBounds();
658 SkRect bounds;
659 viewMatrix.mapRect(&bounds, rrectBounds);
660
661 // Map radius and inset. As the matrix is a similarity matrix, this should be isotropic.
662 SkScalar radius = SkRRectPriv::GetSimpleRadii(rrect).fX;
663 SkScalar matrixFactor = viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewX];
664 SkScalar scaledRadius = SkScalarAbs(radius*matrixFactor);
665 SkScalar scaledInsetWidth = SkScalarAbs(insetWidth*matrixFactor);
666
667 return std::unique_ptr<GrDrawOp>(new ShadowCircularRRectOp(color, bounds,
668 scaledRadius,
669 rrect.isOval(),
670 blurWidth,
671 scaledInsetWidth,
672 blurClamp));
673 }
674 }
675
676 ///////////////////////////////////////////////////////////////////////////////
677
678 #if GR_TEST_UTILS
679
GR_DRAW_OP_TEST_DEFINE(ShadowRRectOp)680 GR_DRAW_OP_TEST_DEFINE(ShadowRRectOp) {
681 // create a similarity matrix
682 SkScalar rotate = random->nextSScalar1() * 360.f;
683 SkScalar translateX = random->nextSScalar1() * 1000.f;
684 SkScalar translateY = random->nextSScalar1() * 1000.f;
685 SkScalar scale = random->nextSScalar1() * 100.f;
686 SkMatrix viewMatrix;
687 viewMatrix.setRotate(rotate);
688 viewMatrix.postTranslate(translateX, translateY);
689 viewMatrix.postScale(scale, scale);
690 SkScalar insetWidth = random->nextSScalar1() * 72.f;
691 SkScalar blurWidth = random->nextSScalar1() * 72.f;
692 SkScalar blurClamp = random->nextSScalar1();
693 bool isCircle = random->nextBool();
694 // This op doesn't use a full GrPaint, just a color.
695 GrColor color = paint.getColor();
696 if (isCircle) {
697 SkRect circle = GrTest::TestSquare(random);
698 SkRRect rrect = SkRRect::MakeOval(circle);
699 return GrShadowRRectOp::Make(color, viewMatrix, rrect, blurWidth, insetWidth, blurClamp);
700 } else {
701 SkRRect rrect;
702 do {
703 // This may return a rrect with elliptical corners, which we don't support.
704 rrect = GrTest::TestRRectSimple(random);
705 } while (!SkRRectPriv::IsSimpleCircular(rrect));
706 return GrShadowRRectOp::Make(color, viewMatrix, rrect, blurWidth, insetWidth, blurClamp);
707 }
708 }
709
710 #endif
711