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 "SkRRect.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 SkFAIL("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 SkFAIL("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 SkFAIL("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 *)257 RequiresDstTexture finalize(const GrCaps&, const GrAppliedClip*) override {
258 return RequiresDstTexture::kNo;
259 }
260
261 private:
262 struct Geometry {
263 GrColor fColor;
264 SkScalar fOuterRadius;
265 SkScalar fUmbraInset;
266 SkScalar fInnerRadius;
267 SkScalar fBlurRadius;
268 SkScalar fClampValue;
269 SkRect fDevBounds;
270 RRectType fType;
271 bool fIsCircle;
272 };
273
274 struct CircleVertex {
275 SkPoint fPos;
276 GrColor fColor;
277 SkPoint fOffset;
278 SkScalar fDistanceCorrection;
279 SkScalar fClampValue;
280 };
281
fillInCircleVerts(const Geometry & args,bool isStroked,CircleVertex ** verts) const282 void fillInCircleVerts(const Geometry& args, bool isStroked, CircleVertex** verts) const {
283
284 GrColor color = args.fColor;
285 SkScalar outerRadius = args.fOuterRadius;
286 SkScalar innerRadius = args.fInnerRadius;
287 SkScalar blurRadius = args.fBlurRadius;
288 SkScalar distanceCorrection = outerRadius / blurRadius;
289 SkScalar clampValue = args.fClampValue;
290
291 const SkRect& bounds = args.fDevBounds;
292
293 // The inner radius in the vertex data must be specified in normalized space.
294 innerRadius = innerRadius / outerRadius;
295
296 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
297 SkScalar halfWidth = 0.5f * bounds.width();
298 SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1
299
300 (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth);
301 (*verts)->fColor = color;
302 (*verts)->fOffset = SkPoint::Make(-octOffset, -1);
303 (*verts)->fDistanceCorrection = distanceCorrection;
304 (*verts)->fClampValue = clampValue;
305 (*verts)++;
306
307 (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth);
308 (*verts)->fColor = color;
309 (*verts)->fOffset = SkPoint::Make(octOffset, -1);
310 (*verts)->fDistanceCorrection = distanceCorrection;
311 (*verts)->fClampValue = clampValue;
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)->fClampValue = clampValue;
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)->fClampValue = clampValue;
326 (*verts)++;
327
328 (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth);
329 (*verts)->fColor = color;
330 (*verts)->fOffset = SkPoint::Make(octOffset, 1);
331 (*verts)->fDistanceCorrection = distanceCorrection;
332 (*verts)->fClampValue = clampValue;
333 (*verts)++;
334
335 (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth);
336 (*verts)->fColor = color;
337 (*verts)->fOffset = SkPoint::Make(-octOffset, 1);
338 (*verts)->fDistanceCorrection = distanceCorrection;
339 (*verts)->fClampValue = clampValue;
340 (*verts)++;
341
342 (*verts)->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth);
343 (*verts)->fColor = color;
344 (*verts)->fOffset = SkPoint::Make(-1, octOffset);
345 (*verts)->fDistanceCorrection = distanceCorrection;
346 (*verts)->fClampValue = clampValue;
347 (*verts)++;
348
349 (*verts)->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth);
350 (*verts)->fColor = color;
351 (*verts)->fOffset = SkPoint::Make(-1, -octOffset);
352 (*verts)->fDistanceCorrection = distanceCorrection;
353 (*verts)->fClampValue = clampValue;
354 (*verts)++;
355
356 if (isStroked) {
357 // compute the inner ring
358
359 // cosine and sine of pi/8
360 SkScalar c = 0.923579533f;
361 SkScalar s = 0.382683432f;
362 SkScalar r = args.fInnerRadius;
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)->fClampValue = clampValue;
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)->fClampValue = clampValue;
376 (*verts)++;
377
378 (*verts)->fPos = center + SkPoint::Make(c * r, -s * r);
379 (*verts)->fColor = color;
380 (*verts)->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius);
381 (*verts)->fDistanceCorrection = distanceCorrection;
382 (*verts)->fClampValue = clampValue;
383 (*verts)++;
384
385 (*verts)->fPos = center + SkPoint::Make(c * r, s * r);
386 (*verts)->fColor = color;
387 (*verts)->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius);
388 (*verts)->fDistanceCorrection = distanceCorrection;
389 (*verts)->fClampValue = clampValue;
390 (*verts)++;
391
392 (*verts)->fPos = center + SkPoint::Make(s * r, c * r);
393 (*verts)->fColor = color;
394 (*verts)->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius);
395 (*verts)->fDistanceCorrection = distanceCorrection;
396 (*verts)->fClampValue = clampValue;
397 (*verts)++;
398
399 (*verts)->fPos = center + SkPoint::Make(-s * r, c * r);
400 (*verts)->fColor = color;
401 (*verts)->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius);
402 (*verts)->fDistanceCorrection = distanceCorrection;
403 (*verts)->fClampValue = clampValue;
404 (*verts)++;
405
406 (*verts)->fPos = center + SkPoint::Make(-c * r, s * r);
407 (*verts)->fColor = color;
408 (*verts)->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius);
409 (*verts)->fDistanceCorrection = distanceCorrection;
410 (*verts)->fClampValue = clampValue;
411 (*verts)++;
412
413 (*verts)->fPos = center + SkPoint::Make(-c * r, -s * r);
414 (*verts)->fColor = color;
415 (*verts)->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius);
416 (*verts)->fDistanceCorrection = distanceCorrection;
417 (*verts)->fClampValue = clampValue;
418 (*verts)++;
419 } else {
420 // filled
421 (*verts)->fPos = center;
422 (*verts)->fColor = color;
423 (*verts)->fOffset = SkPoint::Make(0, 0);
424 (*verts)->fDistanceCorrection = distanceCorrection;
425 (*verts)->fClampValue = clampValue;
426 (*verts)++;
427 }
428 }
429
fillInRRectVerts(const Geometry & args,CircleVertex ** verts) const430 void fillInRRectVerts(const Geometry& args, CircleVertex** verts) const {
431 GrColor color = args.fColor;
432 SkScalar outerRadius = args.fOuterRadius;
433
434 const SkRect& bounds = args.fDevBounds;
435
436 SkScalar umbraInset = args.fUmbraInset;
437 SkScalar minDim = 0.5f*SkTMin(bounds.width(), bounds.height());
438 if (umbraInset > minDim) {
439 umbraInset = minDim;
440 }
441
442 SkScalar xInner[4] = { bounds.fLeft + umbraInset, bounds.fRight - umbraInset,
443 bounds.fLeft + umbraInset, bounds.fRight - umbraInset };
444 SkScalar xMid[4] = { bounds.fLeft + outerRadius, bounds.fRight - outerRadius,
445 bounds.fLeft + outerRadius, bounds.fRight - outerRadius };
446 SkScalar xOuter[4] = { bounds.fLeft, bounds.fRight,
447 bounds.fLeft, bounds.fRight };
448 SkScalar yInner[4] = { bounds.fTop + umbraInset, bounds.fTop + umbraInset,
449 bounds.fBottom - umbraInset, bounds.fBottom - umbraInset };
450 SkScalar yMid[4] = { bounds.fTop + outerRadius, bounds.fTop + outerRadius,
451 bounds.fBottom - outerRadius, bounds.fBottom - outerRadius };
452 SkScalar yOuter[4] = { bounds.fTop, bounds.fTop,
453 bounds.fBottom, bounds.fBottom };
454
455 SkScalar blurRadius = args.fBlurRadius;
456
457 // In the case where we have to inset more for the umbra, our two triangles in the
458 // corner get skewed to a diamond rather than a square. To correct for that,
459 // we also skew the vectors we send to the shader that help define the circle.
460 // By doing so, we end up with a quarter circle in the corner rather than the
461 // elliptical curve.
462 SkVector outerVec = SkVector::Make(0.5f*(outerRadius - umbraInset), -umbraInset);
463 outerVec.normalize();
464 SkVector diagVec = SkVector::Make(outerVec.fX + outerVec.fY,
465 outerVec.fX + outerVec.fY);
466 diagVec *= umbraInset / (2 * umbraInset - outerRadius);
467 SkScalar distanceCorrection = umbraInset / blurRadius;
468 SkScalar clampValue = args.fClampValue;
469
470 // build corner by corner
471 for (int i = 0; i < 4; ++i) {
472 // inner point
473 (*verts)->fPos = SkPoint::Make(xInner[i], yInner[i]);
474 (*verts)->fColor = color;
475 (*verts)->fOffset = SkVector::Make(0, 0);
476 (*verts)->fDistanceCorrection = distanceCorrection;
477 (*verts)->fClampValue = clampValue;
478 (*verts)++;
479
480 // outer points
481 (*verts)->fPos = SkPoint::Make(xOuter[i], yInner[i]);
482 (*verts)->fColor = color;
483 (*verts)->fOffset = SkVector::Make(0, -1);
484 (*verts)->fDistanceCorrection = distanceCorrection;
485 (*verts)->fClampValue = clampValue;
486 (*verts)++;
487
488 (*verts)->fPos = SkPoint::Make(xOuter[i], yMid[i]);
489 (*verts)->fColor = color;
490 (*verts)->fOffset = outerVec;
491 (*verts)->fDistanceCorrection = distanceCorrection;
492 (*verts)->fClampValue = clampValue;
493 (*verts)++;
494
495 (*verts)->fPos = SkPoint::Make(xOuter[i], yOuter[i]);
496 (*verts)->fColor = color;
497 (*verts)->fOffset = diagVec;
498 (*verts)->fDistanceCorrection = distanceCorrection;
499 (*verts)->fClampValue = clampValue;
500 (*verts)++;
501
502 (*verts)->fPos = SkPoint::Make(xMid[i], yOuter[i]);
503 (*verts)->fColor = color;
504 (*verts)->fOffset = outerVec;
505 (*verts)->fDistanceCorrection = distanceCorrection;
506 (*verts)->fClampValue = clampValue;
507 (*verts)++;
508
509 (*verts)->fPos = SkPoint::Make(xInner[i], yOuter[i]);
510 (*verts)->fColor = color;
511 (*verts)->fOffset = SkVector::Make(0, -1);
512 (*verts)->fDistanceCorrection = distanceCorrection;
513 (*verts)->fClampValue = clampValue;
514 (*verts)++;
515 }
516
517 // Add the additional vertices for overstroked rrects.
518 // Effectively this is an additional stroked rrect, with its
519 // parameters equal to those in the center of the 9-patch. This will
520 // give constant values across this inner ring.
521 if (kOverstroke_RRectType == args.fType) {
522 SkASSERT(args.fInnerRadius > 0.0f);
523
524 SkScalar inset = umbraInset + args.fInnerRadius;
525
526 // TL
527 (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fTop + inset);
528 (*verts)->fColor = color;
529 (*verts)->fOffset = SkPoint::Make(0, 0);
530 (*verts)->fDistanceCorrection = distanceCorrection;
531 (*verts)->fClampValue = clampValue;
532 (*verts)++;
533
534 // TR
535 (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fTop + inset);
536 (*verts)->fColor = color;
537 (*verts)->fOffset = SkPoint::Make(0, 0);
538 (*verts)->fDistanceCorrection = distanceCorrection;
539 (*verts)->fClampValue = clampValue;
540 (*verts)++;
541
542 // BL
543 (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fBottom - inset);
544 (*verts)->fColor = color;
545 (*verts)->fOffset = SkPoint::Make(0, 0);
546 (*verts)->fDistanceCorrection = distanceCorrection;
547 (*verts)->fClampValue = clampValue;
548 (*verts)++;
549
550 // BR
551 (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fBottom - inset);
552 (*verts)->fColor = color;
553 (*verts)->fOffset = SkPoint::Make(0, 0);
554 (*verts)->fDistanceCorrection = distanceCorrection;
555 (*verts)->fClampValue = clampValue;
556 (*verts)++;
557 }
558
559 }
560
onPrepareDraws(Target * target) const561 void onPrepareDraws(Target* target) const override {
562 // Setup geometry processor
563 sk_sp<GrGeometryProcessor> gp = GrRRectShadowGeoProc::Make();
564
565 int instanceCount = fGeoData.count();
566 size_t vertexStride = gp->getVertexStride();
567 SkASSERT(sizeof(CircleVertex) == vertexStride);
568
569 const GrBuffer* vertexBuffer;
570 int firstVertex;
571 CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(vertexStride, fVertCount,
572 &vertexBuffer, &firstVertex);
573 if (!verts) {
574 SkDebugf("Could not allocate vertices\n");
575 return;
576 }
577
578 const GrBuffer* indexBuffer = nullptr;
579 int firstIndex = 0;
580 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
581 if (!indices) {
582 SkDebugf("Could not allocate indices\n");
583 return;
584 }
585
586 int currStartVertex = 0;
587 for (int i = 0; i < instanceCount; i++) {
588 const Geometry& args = fGeoData[i];
589
590 if (args.fIsCircle) {
591 bool isStroked = SkToBool(kStroke_RRectType == args.fType);
592 this->fillInCircleVerts(args, isStroked, &verts);
593
594 const uint16_t* primIndices = circle_type_to_indices(isStroked);
595 const int primIndexCount = circle_type_to_index_count(isStroked);
596 for (int i = 0; i < primIndexCount; ++i) {
597 *indices++ = primIndices[i] + currStartVertex;
598 }
599
600 currStartVertex += circle_type_to_vert_count(isStroked);
601
602 } else {
603 this->fillInRRectVerts(args, &verts);
604
605 const uint16_t* primIndices = rrect_type_to_indices(args.fType);
606 const int primIndexCount = rrect_type_to_index_count(args.fType);
607 for (int i = 0; i < primIndexCount; ++i) {
608 *indices++ = primIndices[i] + currStartVertex;
609 }
610
611 currStartVertex += rrect_type_to_vert_count(args.fType);
612 }
613 }
614
615 static const uint32_t kPipelineFlags = 0;
616 const GrPipeline* pipeline =
617 target->makePipeline(kPipelineFlags, &GrProcessorSet::EmptySet());
618
619 GrMesh mesh(GrPrimitiveType::kTriangles);
620 mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1);
621 mesh.setVertexData(vertexBuffer, firstVertex);
622 target->draw(gp.get(), pipeline, mesh);
623 }
624
onCombineIfPossible(GrOp * t,const GrCaps & caps)625 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
626 ShadowCircularRRectOp* that = t->cast<ShadowCircularRRectOp>();
627 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
628 this->joinBounds(*that);
629 fVertCount += that->fVertCount;
630 fIndexCount += that->fIndexCount;
631 return true;
632 }
633
634 SkSTArray<1, Geometry, true> fGeoData;
635 int fVertCount;
636 int fIndexCount;
637
638 typedef GrMeshDrawOp INHERITED;
639 };
640
641 } // anonymous namespace
642
643 ///////////////////////////////////////////////////////////////////////////////
644
645 namespace GrShadowRRectOp {
Make(GrColor color,const SkMatrix & viewMatrix,const SkRRect & rrect,SkScalar blurWidth,SkScalar insetWidth,SkScalar blurClamp)646 std::unique_ptr<GrDrawOp> Make(GrColor color,
647 const SkMatrix& viewMatrix,
648 const SkRRect& rrect,
649 SkScalar blurWidth,
650 SkScalar insetWidth,
651 SkScalar blurClamp) {
652 // Shadow rrect ops only handle simple circular rrects.
653 SkASSERT(viewMatrix.isSimilarity() &&
654 (rrect.isSimpleCircular() || rrect.isRect() || rrect.isCircle()));
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 = rrect.getSimpleRadii().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 (!rrect.isSimpleCircular());
706 return GrShadowRRectOp::Make(color, viewMatrix, rrect, blurWidth, insetWidth, blurClamp);
707 }
708 }
709
710 #endif
711