Line data Source code
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 :
10 : #include "GrDrawOpTest.h"
11 : #include "GrOpFlushState.h"
12 : #include "GrResourceProvider.h"
13 : #include "GrStyle.h"
14 :
15 : #include "effects/GrShadowGeoProc.h"
16 :
17 : ///////////////////////////////////////////////////////////////////////////////
18 :
19 : // We have two possible cases for geometry for a circle:
20 :
21 : // In the case of a normal fill, we draw geometry for the circle as an octagon.
22 : static const uint16_t gFillCircleIndices[] = {
23 : // enter the octagon
24 : // clang-format off
25 : 0, 1, 8, 1, 2, 8,
26 : 2, 3, 8, 3, 4, 8,
27 : 4, 5, 8, 5, 6, 8,
28 : 6, 7, 8, 7, 0, 8,
29 : // clang-format on
30 : };
31 :
32 : // For stroked circles, we use two nested octagons.
33 : static const uint16_t gStrokeCircleIndices[] = {
34 : // enter the octagon
35 : // clang-format off
36 : 0, 1, 9, 0, 9, 8,
37 : 1, 2, 10, 1, 10, 9,
38 : 2, 3, 11, 2, 11, 10,
39 : 3, 4, 12, 3, 12, 11,
40 : 4, 5, 13, 4, 13, 12,
41 : 5, 6, 14, 5, 14, 13,
42 : 6, 7, 15, 6, 15, 14,
43 : 7, 0, 8, 7, 8, 15,
44 : // clang-format on
45 : };
46 :
47 : static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
48 : static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
49 : static const int kVertsPerStrokeCircle = 16;
50 : static const int kVertsPerFillCircle = 9;
51 :
52 0 : static int circle_type_to_vert_count(bool stroked) {
53 0 : return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
54 : }
55 :
56 0 : static int circle_type_to_index_count(bool stroked) {
57 0 : return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
58 : }
59 :
60 0 : static const uint16_t* circle_type_to_indices(bool stroked) {
61 0 : return stroked ? gStrokeCircleIndices : gFillCircleIndices;
62 : }
63 :
64 : ///////////////////////////////////////////////////////////////////////////////
65 :
66 0 : class ShadowCircleOp final : public GrLegacyMeshDrawOp {
67 : public:
68 0 : DEFINE_OP_CLASS_ID
69 :
70 0 : static std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
71 : SkPoint center, SkScalar radius,
72 : SkScalar blurRadius, const GrStyle& style) {
73 0 : SkASSERT(viewMatrix.isSimilarity());
74 0 : const SkStrokeRec& stroke = style.strokeRec();
75 0 : if (style.hasPathEffect()) {
76 0 : return nullptr;
77 : }
78 0 : SkStrokeRec::Style recStyle = stroke.getStyle();
79 :
80 0 : viewMatrix.mapPoints(¢er, 1);
81 0 : radius = viewMatrix.mapRadius(radius);
82 0 : SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
83 :
84 : bool isStrokeOnly =
85 0 : SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
86 0 : bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
87 :
88 0 : SkScalar innerRadius = -SK_ScalarHalf;
89 0 : SkScalar outerRadius = radius;
90 0 : SkScalar halfWidth = 0;
91 0 : if (hasStroke) {
92 0 : if (SkScalarNearlyZero(strokeWidth)) {
93 0 : halfWidth = SK_ScalarHalf;
94 : } else {
95 0 : halfWidth = SkScalarHalf(strokeWidth);
96 : }
97 :
98 0 : outerRadius += halfWidth;
99 0 : if (isStrokeOnly) {
100 0 : innerRadius = radius - halfWidth;
101 : }
102 : }
103 :
104 0 : bool stroked = isStrokeOnly && innerRadius > 0.0f;
105 0 : std::unique_ptr<ShadowCircleOp> op(new ShadowCircleOp());
106 0 : op->fViewMatrixIfUsingLocalCoords = viewMatrix;
107 :
108 0 : SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
109 0 : center.fX + outerRadius, center.fY + outerRadius);
110 :
111 0 : op->fCircles.emplace_back(
112 0 : Circle{color, outerRadius, innerRadius, blurRadius, devBounds, stroked});
113 :
114 : // Use the original radius and stroke radius for the bounds so that it does not include the
115 : // AA bloat.
116 0 : radius += halfWidth;
117 0 : op->setBounds(
118 0 : {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
119 0 : HasAABloat::kNo, IsZeroArea::kNo);
120 0 : op->fVertCount = circle_type_to_vert_count(stroked);
121 0 : op->fIndexCount = circle_type_to_index_count(stroked);
122 0 : return std::move(op);
123 : }
124 :
125 0 : const char* name() const override { return "ShadowCircleOp"; }
126 :
127 0 : SkString dumpInfo() const override {
128 0 : SkString string;
129 0 : for (int i = 0; i < fCircles.count(); ++i) {
130 0 : string.appendf(
131 : "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
132 : "OuterRad: %.2f, InnerRad: %.2f, BlurRad: %.2f\n",
133 0 : fCircles[i].fColor, fCircles[i].fDevBounds.fLeft, fCircles[i].fDevBounds.fTop,
134 0 : fCircles[i].fDevBounds.fRight, fCircles[i].fDevBounds.fBottom,
135 0 : fCircles[i].fOuterRadius, fCircles[i].fInnerRadius, fCircles[i].fBlurRadius);
136 : }
137 0 : string.append(DumpPipelineInfo(*this->pipeline()));
138 0 : string.append(INHERITED::dumpInfo());
139 0 : return string;
140 : }
141 :
142 : private:
143 0 : ShadowCircleOp() : INHERITED(ClassID()) {}
144 :
145 0 : void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
146 : GrProcessorAnalysisCoverage* coverage) const override {
147 0 : color->setToConstant(fCircles[0].fColor);
148 0 : *coverage = GrProcessorAnalysisCoverage::kSingleChannel;
149 0 : }
150 :
151 0 : void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override {
152 0 : optimizations.getOverrideColorIfSet(&fCircles[0].fColor);
153 0 : if (!optimizations.readsLocalCoords()) {
154 0 : fViewMatrixIfUsingLocalCoords.reset();
155 : }
156 0 : }
157 :
158 0 : void onPrepareDraws(Target* target) const override {
159 : SkMatrix localMatrix;
160 0 : if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
161 0 : return;
162 : }
163 :
164 : // Setup geometry processor
165 0 : sk_sp<GrGeometryProcessor> gp(GrRRectShadowGeoProc::Make(localMatrix));
166 :
167 : struct CircleVertex {
168 : SkPoint fPos;
169 : GrColor fColor;
170 : SkPoint fOffset;
171 : SkScalar fOuterRadius;
172 : SkScalar fBlurRadius;
173 : };
174 :
175 0 : int instanceCount = fCircles.count();
176 0 : size_t vertexStride = gp->getVertexStride();
177 0 : SkASSERT(vertexStride == sizeof(CircleVertex));
178 :
179 : const GrBuffer* vertexBuffer;
180 : int firstVertex;
181 0 : char* vertices = (char*)target->makeVertexSpace(vertexStride, fVertCount, &vertexBuffer,
182 0 : &firstVertex);
183 0 : if (!vertices) {
184 0 : SkDebugf("Could not allocate vertices\n");
185 0 : return;
186 : }
187 :
188 0 : const GrBuffer* indexBuffer = nullptr;
189 0 : int firstIndex = 0;
190 0 : uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
191 0 : if (!indices) {
192 0 : SkDebugf("Could not allocate indices\n");
193 0 : return;
194 : }
195 :
196 0 : int currStartVertex = 0;
197 0 : for (int i = 0; i < instanceCount; i++) {
198 0 : const Circle& circle = fCircles[i];
199 :
200 0 : GrColor color = circle.fColor;
201 0 : SkScalar outerRadius = circle.fOuterRadius;
202 0 : SkScalar innerRadius = circle.fInnerRadius;
203 0 : SkScalar blurRadius = circle.fBlurRadius;
204 :
205 0 : const SkRect& bounds = circle.fDevBounds;
206 0 : CircleVertex* ov0 = reinterpret_cast<CircleVertex*>(vertices + 0 * vertexStride);
207 0 : CircleVertex* ov1 = reinterpret_cast<CircleVertex*>(vertices + 1 * vertexStride);
208 0 : CircleVertex* ov2 = reinterpret_cast<CircleVertex*>(vertices + 2 * vertexStride);
209 0 : CircleVertex* ov3 = reinterpret_cast<CircleVertex*>(vertices + 3 * vertexStride);
210 0 : CircleVertex* ov4 = reinterpret_cast<CircleVertex*>(vertices + 4 * vertexStride);
211 0 : CircleVertex* ov5 = reinterpret_cast<CircleVertex*>(vertices + 5 * vertexStride);
212 0 : CircleVertex* ov6 = reinterpret_cast<CircleVertex*>(vertices + 6 * vertexStride);
213 0 : CircleVertex* ov7 = reinterpret_cast<CircleVertex*>(vertices + 7 * vertexStride);
214 :
215 : // The inner radius in the vertex data must be specified in normalized space.
216 0 : innerRadius = innerRadius / outerRadius;
217 :
218 0 : SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
219 0 : SkScalar halfWidth = 0.5f * bounds.width();
220 0 : SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1
221 :
222 0 : ov0->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth);
223 0 : ov0->fColor = color;
224 0 : ov0->fOffset = SkPoint::Make(-octOffset, -1);
225 0 : ov0->fOuterRadius = outerRadius;
226 0 : ov0->fBlurRadius = blurRadius;
227 :
228 0 : ov1->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth);
229 0 : ov1->fColor = color;
230 0 : ov1->fOffset = SkPoint::Make(octOffset, -1);
231 0 : ov1->fOuterRadius = outerRadius;
232 0 : ov1->fBlurRadius = blurRadius;
233 :
234 0 : ov2->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth);
235 0 : ov2->fColor = color;
236 0 : ov2->fOffset = SkPoint::Make(1, -octOffset);
237 0 : ov2->fOuterRadius = outerRadius;
238 0 : ov2->fBlurRadius = blurRadius;
239 :
240 0 : ov3->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth);
241 0 : ov3->fColor = color;
242 0 : ov3->fOffset = SkPoint::Make(1, octOffset);
243 0 : ov3->fOuterRadius = outerRadius;
244 0 : ov3->fBlurRadius = blurRadius;
245 :
246 0 : ov4->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth);
247 0 : ov4->fColor = color;
248 0 : ov4->fOffset = SkPoint::Make(octOffset, 1);
249 0 : ov4->fOuterRadius = outerRadius;
250 0 : ov4->fBlurRadius = blurRadius;
251 :
252 0 : ov5->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth);
253 0 : ov5->fColor = color;
254 0 : ov5->fOffset = SkPoint::Make(-octOffset, 1);
255 0 : ov5->fOuterRadius = outerRadius;
256 0 : ov5->fBlurRadius = blurRadius;
257 :
258 0 : ov6->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth);
259 0 : ov6->fColor = color;
260 0 : ov6->fOffset = SkPoint::Make(-1, octOffset);
261 0 : ov6->fOuterRadius = outerRadius;
262 0 : ov6->fBlurRadius = blurRadius;
263 :
264 0 : ov7->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth);
265 0 : ov7->fColor = color;
266 0 : ov7->fOffset = SkPoint::Make(-1, -octOffset);
267 0 : ov7->fOuterRadius = outerRadius;
268 0 : ov7->fBlurRadius = blurRadius;
269 :
270 0 : if (circle.fStroked) {
271 : // compute the inner ring
272 0 : CircleVertex* iv0 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
273 0 : CircleVertex* iv1 = reinterpret_cast<CircleVertex*>(vertices + 9 * vertexStride);
274 0 : CircleVertex* iv2 = reinterpret_cast<CircleVertex*>(vertices + 10 * vertexStride);
275 0 : CircleVertex* iv3 = reinterpret_cast<CircleVertex*>(vertices + 11 * vertexStride);
276 0 : CircleVertex* iv4 = reinterpret_cast<CircleVertex*>(vertices + 12 * vertexStride);
277 0 : CircleVertex* iv5 = reinterpret_cast<CircleVertex*>(vertices + 13 * vertexStride);
278 0 : CircleVertex* iv6 = reinterpret_cast<CircleVertex*>(vertices + 14 * vertexStride);
279 0 : CircleVertex* iv7 = reinterpret_cast<CircleVertex*>(vertices + 15 * vertexStride);
280 :
281 : // cosine and sine of pi/8
282 0 : SkScalar c = 0.923579533f;
283 0 : SkScalar s = 0.382683432f;
284 0 : SkScalar r = circle.fInnerRadius;
285 :
286 0 : iv0->fPos = center + SkPoint::Make(-s * r, -c * r);
287 0 : iv0->fColor = color;
288 0 : iv0->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius);
289 0 : iv0->fOuterRadius = outerRadius;
290 0 : iv0->fBlurRadius = blurRadius;
291 :
292 0 : iv1->fPos = center + SkPoint::Make(s * r, -c * r);
293 0 : iv1->fColor = color;
294 0 : iv1->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius);
295 0 : iv1->fOuterRadius = outerRadius;
296 0 : iv1->fBlurRadius = blurRadius;
297 :
298 0 : iv2->fPos = center + SkPoint::Make(c * r, -s * r);
299 0 : iv2->fColor = color;
300 0 : iv2->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius);
301 0 : iv2->fOuterRadius = outerRadius;
302 0 : iv2->fBlurRadius = blurRadius;
303 :
304 0 : iv3->fPos = center + SkPoint::Make(c * r, s * r);
305 0 : iv3->fColor = color;
306 0 : iv3->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius);
307 0 : iv3->fOuterRadius = outerRadius;
308 0 : iv3->fBlurRadius = blurRadius;
309 :
310 0 : iv4->fPos = center + SkPoint::Make(s * r, c * r);
311 0 : iv4->fColor = color;
312 0 : iv4->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius);
313 0 : iv4->fOuterRadius = outerRadius;
314 0 : iv4->fBlurRadius = blurRadius;
315 :
316 0 : iv5->fPos = center + SkPoint::Make(-s * r, c * r);
317 0 : iv5->fColor = color;
318 0 : iv5->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius);
319 0 : iv5->fOuterRadius = outerRadius;
320 0 : iv5->fBlurRadius = blurRadius;
321 :
322 0 : iv6->fPos = center + SkPoint::Make(-c * r, s * r);
323 0 : iv6->fColor = color;
324 0 : iv6->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius);
325 0 : iv6->fOuterRadius = outerRadius;
326 0 : iv6->fBlurRadius = blurRadius;
327 :
328 0 : iv7->fPos = center + SkPoint::Make(-c * r, -s * r);
329 0 : iv7->fColor = color;
330 0 : iv7->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius);
331 0 : iv7->fOuterRadius = outerRadius;
332 0 : iv7->fBlurRadius = blurRadius;
333 : } else {
334 : // filled
335 0 : CircleVertex* iv = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
336 0 : iv->fPos = center;
337 0 : iv->fColor = color;
338 0 : iv->fOffset = SkPoint::Make(0, 0);
339 0 : iv->fOuterRadius = outerRadius;
340 0 : iv->fBlurRadius = blurRadius;
341 : }
342 :
343 0 : const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
344 0 : const int primIndexCount = circle_type_to_index_count(circle.fStroked);
345 0 : for (int i = 0; i < primIndexCount; ++i) {
346 0 : *indices++ = primIndices[i] + currStartVertex;
347 : }
348 :
349 0 : currStartVertex += circle_type_to_vert_count(circle.fStroked);
350 0 : vertices += circle_type_to_vert_count(circle.fStroked) * vertexStride;
351 : }
352 :
353 0 : GrMesh mesh;
354 : mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
355 0 : firstIndex, fVertCount, fIndexCount);
356 0 : target->draw(gp.get(), this->pipeline(), mesh);
357 : }
358 :
359 0 : bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
360 0 : ShadowCircleOp* that = t->cast<ShadowCircleOp>();
361 0 : if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
362 : that->bounds(), caps)) {
363 0 : return false;
364 : }
365 :
366 0 : if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
367 0 : return false;
368 : }
369 :
370 0 : fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
371 0 : this->joinBounds(*that);
372 0 : fVertCount += that->fVertCount;
373 0 : fIndexCount += that->fIndexCount;
374 0 : return true;
375 : }
376 :
377 : struct Circle {
378 : GrColor fColor;
379 : SkScalar fOuterRadius;
380 : SkScalar fInnerRadius;
381 : SkScalar fBlurRadius;
382 : SkRect fDevBounds;
383 : bool fStroked;
384 : };
385 :
386 : SkSTArray<1, Circle, true> fCircles;
387 : SkMatrix fViewMatrixIfUsingLocalCoords;
388 : int fVertCount;
389 : int fIndexCount;
390 :
391 : typedef GrLegacyMeshDrawOp INHERITED;
392 : };
393 :
394 : ///////////////////////////////////////////////////////////////////////////////////////////////////
395 :
396 : // We have two possible cases for geometry for a shadow roundrect.
397 : //
398 : // In the case of a normal stroke, we draw the roundrect as a 9-patch without the center quad.
399 : // ____________
400 : // |_|________|_|
401 : // | | | |
402 : // | | | |
403 : // | | | |
404 : // |_|________|_|
405 : // |_|________|_|
406 : //
407 : // In the case where the stroke width is greater than twice the corner radius (overstroke),
408 : // we add additional geometry to mark out the rectangle in the center. The shared vertices
409 : // are duplicated so we can set a different outer radius for the fill calculation.
410 : // ____________
411 : // |_|________|_|
412 : // | |\ ____ /| |
413 : // | | | | | |
414 : // | | |____| | |
415 : // |_|/______\|_|
416 : // |_|________|_|
417 : //
418 : // For filled rrects we reuse the overstroke geometry but make the inner rect degenerate
419 : // (either a point or a horizontal or vertical line).
420 :
421 : static const uint16_t gOverstrokeRRectIndices[] = {
422 : // clang-format off
423 : // corners
424 : 0, 1, 5, 0, 5, 4,
425 : 2, 3, 7, 2, 7, 6,
426 : 8, 9, 13, 8, 13, 12,
427 : 10, 11, 15, 10, 15, 14,
428 :
429 : // edges
430 : 1, 2, 6, 1, 6, 5,
431 : 4, 5, 9, 4, 9, 8,
432 : 6, 7, 11, 6, 11, 10,
433 : 9, 10, 14, 9, 14, 13,
434 :
435 : // overstroke quads
436 : // we place this at the end so that we can skip these indices when rendering as stroked
437 : 16, 17, 19, 16, 19, 18,
438 : 19, 17, 23, 19, 23, 21,
439 : 21, 23, 22, 21, 22, 20,
440 : 22, 16, 18, 22, 18, 20,
441 : // clang-format on
442 : };
443 : // standard stroke indices start at the same place, but will skip the overstroke "ring"
444 : static const uint16_t* gStrokeRRectIndices = gOverstrokeRRectIndices;
445 :
446 : // overstroke count
447 : static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices);
448 : // simple stroke count skips overstroke indices
449 : static const int kIndicesPerStrokeRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
450 : static const int kVertsPerStrokeRRect = 16;
451 : static const int kVertsPerOverstrokeRRect = 24;
452 :
453 : enum RRectType {
454 : kFill_RRectType,
455 : kStroke_RRectType,
456 : kOverstroke_RRectType,
457 : };
458 :
459 0 : static int rrect_type_to_vert_count(RRectType type) {
460 0 : switch (type) {
461 : case kFill_RRectType:
462 0 : return kVertsPerOverstrokeRRect;
463 : case kStroke_RRectType:
464 0 : return kVertsPerStrokeRRect;
465 : case kOverstroke_RRectType:
466 0 : return kVertsPerOverstrokeRRect;
467 : }
468 0 : SkFAIL("Invalid type");
469 0 : return 0;
470 : }
471 :
472 0 : static int rrect_type_to_index_count(RRectType type) {
473 0 : switch (type) {
474 : case kFill_RRectType:
475 0 : return kIndicesPerOverstrokeRRect;
476 : case kStroke_RRectType:
477 0 : return kIndicesPerStrokeRRect;
478 : case kOverstroke_RRectType:
479 0 : return kIndicesPerOverstrokeRRect;
480 : }
481 0 : SkFAIL("Invalid type");
482 0 : return 0;
483 : }
484 :
485 0 : static const uint16_t* rrect_type_to_indices(RRectType type) {
486 0 : switch (type) {
487 : case kFill_RRectType:
488 0 : return gOverstrokeRRectIndices;
489 : case kStroke_RRectType:
490 0 : return gStrokeRRectIndices;
491 : case kOverstroke_RRectType:
492 0 : return gOverstrokeRRectIndices;
493 : }
494 0 : SkFAIL("Invalid type");
495 0 : return nullptr;
496 : }
497 :
498 : // For distance computations in the interior of filled rrects we:
499 : //
500 : // add a interior degenerate (point or line) rect
501 : // each vertex of that rect gets -outerRad as its radius
502 : // this makes the computation of the distance to the outer edge be negative
503 : // negative values are caught and then handled differently in the GP's onEmitCode
504 : // each vertex is also given the normalized x & y distance from the interior rect's edge
505 : // the GP takes the min of those depths +1 to get the normalized distance to the outer edge
506 :
507 0 : class ShadowCircularRRectOp final : public GrLegacyMeshDrawOp {
508 : public:
509 0 : DEFINE_OP_CLASS_ID
510 :
511 : // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
512 : // whether the rrect is only stroked or stroked and filled.
513 0 : ShadowCircularRRectOp(GrColor color, const SkMatrix& viewMatrix, const SkRect& devRect,
514 : float devRadius, float blurRadius, float devStrokeWidth, bool strokeOnly)
515 0 : : INHERITED(ClassID()), fViewMatrixIfUsingLocalCoords(viewMatrix) {
516 0 : SkRect bounds = devRect;
517 0 : SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
518 0 : SkScalar innerRadius = 0.0f;
519 0 : SkScalar outerRadius = devRadius;
520 0 : SkScalar halfWidth = 0;
521 0 : RRectType type = kFill_RRectType;
522 0 : if (devStrokeWidth > 0) {
523 0 : if (SkScalarNearlyZero(devStrokeWidth)) {
524 0 : halfWidth = SK_ScalarHalf;
525 : } else {
526 0 : halfWidth = SkScalarHalf(devStrokeWidth);
527 : }
528 :
529 0 : if (strokeOnly) {
530 : // If stroke is greater than width or height, this is still a fill
531 : // Otherwise we compute stroke params
532 0 : if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
533 0 : innerRadius = devRadius - halfWidth;
534 0 : type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
535 : }
536 : }
537 0 : outerRadius += halfWidth;
538 0 : bounds.outset(halfWidth, halfWidth);
539 : }
540 :
541 0 : this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
542 :
543 0 : fGeoData.emplace_back(Geometry{color, outerRadius, innerRadius, blurRadius, bounds, type});
544 0 : fVertCount = rrect_type_to_vert_count(type);
545 0 : fIndexCount = rrect_type_to_index_count(type);
546 0 : }
547 :
548 0 : const char* name() const override { return "ShadowCircularRRectOp"; }
549 :
550 0 : SkString dumpInfo() const override {
551 0 : SkString string;
552 0 : for (int i = 0; i < fGeoData.count(); ++i) {
553 0 : string.appendf(
554 : "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
555 : "OuterRad: %.2f, InnerRad: %.2f, BlurRad: %.2f\n",
556 0 : fGeoData[i].fColor, fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
557 0 : fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
558 0 : fGeoData[i].fOuterRadius, fGeoData[i].fInnerRadius, fGeoData[i].fBlurRadius);
559 : }
560 0 : string.append(DumpPipelineInfo(*this->pipeline()));
561 0 : string.append(INHERITED::dumpInfo());
562 0 : return string;
563 : }
564 :
565 : private:
566 0 : void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
567 : GrProcessorAnalysisCoverage* coverage) const override {
568 0 : color->setToConstant(fGeoData[0].fColor);
569 0 : *coverage = GrProcessorAnalysisCoverage::kSingleChannel;
570 0 : }
571 :
572 0 : void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override {
573 0 : optimizations.getOverrideColorIfSet(&fGeoData[0].fColor);
574 0 : if (!optimizations.readsLocalCoords()) {
575 0 : fViewMatrixIfUsingLocalCoords.reset();
576 : }
577 0 : }
578 :
579 : struct CircleVertex {
580 : SkPoint fPos;
581 : GrColor fColor;
582 : SkPoint fOffset;
583 : SkScalar fOuterRadius;
584 : SkScalar fBlurRadius;
585 : };
586 :
587 0 : static void FillInOverstrokeVerts(CircleVertex** verts, const SkRect& bounds, SkScalar smInset,
588 : SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
589 : GrColor color, SkScalar blurRadius) {
590 0 : SkASSERT(smInset < bigInset);
591 :
592 : // TL
593 0 : (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fTop + smInset);
594 0 : (*verts)->fColor = color;
595 0 : (*verts)->fOffset = SkPoint::Make(xOffset, 0);
596 0 : (*verts)->fOuterRadius = outerRadius;
597 0 : (*verts)->fBlurRadius = blurRadius;
598 0 : (*verts)++;
599 :
600 : // TR
601 0 : (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fTop + smInset);
602 0 : (*verts)->fColor = color;
603 0 : (*verts)->fOffset = SkPoint::Make(xOffset, 0);
604 0 : (*verts)->fOuterRadius = outerRadius;
605 0 : (*verts)->fBlurRadius = blurRadius;
606 0 : (*verts)++;
607 :
608 0 : (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fTop + bigInset);
609 0 : (*verts)->fColor = color;
610 0 : (*verts)->fOffset = SkPoint::Make(0, 0);
611 0 : (*verts)->fOuterRadius = outerRadius;
612 0 : (*verts)->fBlurRadius = blurRadius;
613 0 : (*verts)++;
614 :
615 0 : (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fTop + bigInset);
616 0 : (*verts)->fColor = color;
617 0 : (*verts)->fOffset = SkPoint::Make(0, 0);
618 0 : (*verts)->fOuterRadius = outerRadius;
619 0 : (*verts)->fBlurRadius = blurRadius;
620 0 : (*verts)++;
621 :
622 0 : (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fBottom - bigInset);
623 0 : (*verts)->fColor = color;
624 0 : (*verts)->fOffset = SkPoint::Make(0, 0);
625 0 : (*verts)->fOuterRadius = outerRadius;
626 0 : (*verts)->fBlurRadius = blurRadius;
627 0 : (*verts)++;
628 :
629 0 : (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fBottom - bigInset);
630 0 : (*verts)->fColor = color;
631 0 : (*verts)->fOffset = SkPoint::Make(0, 0);
632 0 : (*verts)->fOuterRadius = outerRadius;
633 0 : (*verts)->fBlurRadius = blurRadius;
634 0 : (*verts)++;
635 :
636 : // BL
637 0 : (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fBottom - smInset);
638 0 : (*verts)->fColor = color;
639 0 : (*verts)->fOffset = SkPoint::Make(xOffset, 0);
640 0 : (*verts)->fOuterRadius = outerRadius;
641 0 : (*verts)->fBlurRadius = blurRadius;
642 0 : (*verts)++;
643 :
644 : // BR
645 0 : (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fBottom - smInset);
646 0 : (*verts)->fColor = color;
647 0 : (*verts)->fOffset = SkPoint::Make(xOffset, 0);
648 0 : (*verts)->fOuterRadius = outerRadius;
649 0 : (*verts)->fBlurRadius = blurRadius;
650 0 : (*verts)++;
651 0 : }
652 :
653 0 : void onPrepareDraws(Target* target) const override {
654 : // Invert the view matrix as a local matrix (if any other processors require coords).
655 : SkMatrix localMatrix;
656 0 : if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
657 0 : return;
658 : }
659 :
660 : // Setup geometry processor
661 0 : sk_sp<GrGeometryProcessor> gp(GrRRectShadowGeoProc::Make(localMatrix));
662 :
663 0 : int instanceCount = fGeoData.count();
664 0 : size_t vertexStride = gp->getVertexStride();
665 0 : SkASSERT(sizeof(CircleVertex) == vertexStride);
666 :
667 : const GrBuffer* vertexBuffer;
668 : int firstVertex;
669 :
670 0 : CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(vertexStride, fVertCount,
671 0 : &vertexBuffer, &firstVertex);
672 0 : if (!verts) {
673 0 : SkDebugf("Could not allocate vertices\n");
674 0 : return;
675 : }
676 :
677 0 : const GrBuffer* indexBuffer = nullptr;
678 0 : int firstIndex = 0;
679 0 : uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
680 0 : if (!indices) {
681 0 : SkDebugf("Could not allocate indices\n");
682 0 : return;
683 : }
684 :
685 0 : int currStartVertex = 0;
686 0 : for (int i = 0; i < instanceCount; i++) {
687 0 : const Geometry& args = fGeoData[i];
688 :
689 0 : GrColor color = args.fColor;
690 0 : SkScalar outerRadius = args.fOuterRadius;
691 :
692 0 : const SkRect& bounds = args.fDevBounds;
693 :
694 0 : SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
695 0 : bounds.fBottom - outerRadius, bounds.fBottom};
696 :
697 0 : SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
698 : // The inner radius in the vertex data must be specified in normalized space.
699 : // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
700 0 : SkScalar blurRadius = args.fBlurRadius;
701 0 : for (int i = 0; i < 4; ++i) {
702 0 : verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
703 0 : verts->fColor = color;
704 0 : verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
705 0 : verts->fOuterRadius = outerRadius;
706 0 : verts->fBlurRadius = blurRadius;
707 0 : verts++;
708 :
709 0 : verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
710 0 : verts->fColor = color;
711 0 : verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
712 0 : verts->fOuterRadius = outerRadius;
713 0 : verts->fBlurRadius = blurRadius;
714 0 : verts++;
715 :
716 0 : verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
717 0 : verts->fColor = color;
718 0 : verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
719 0 : verts->fOuterRadius = outerRadius;
720 0 : verts->fBlurRadius = blurRadius;
721 0 : verts++;
722 :
723 0 : verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
724 0 : verts->fColor = color;
725 0 : verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
726 0 : verts->fOuterRadius = outerRadius;
727 0 : verts->fBlurRadius = blurRadius;
728 0 : verts++;
729 : }
730 : // Add the additional vertices for overstroked rrects.
731 : // Effectively this is an additional stroked rrect, with its
732 : // outer radius = outerRadius - innerRadius, and inner radius = 0.
733 : // This will give us correct AA in the center and the correct
734 : // distance to the outer edge.
735 : //
736 : // Also, the outer offset is a constant vector pointing to the right, which
737 : // guarantees that the distance value along the outer rectangle is constant.
738 0 : if (kOverstroke_RRectType == args.fType) {
739 0 : SkASSERT(args.fInnerRadius <= 0.0f);
740 :
741 0 : SkScalar overstrokeOuterRadius = outerRadius - args.fInnerRadius;
742 : // this is the normalized distance from the outer rectangle of this
743 : // geometry to the outer edge
744 0 : SkScalar maxOffset = -args.fInnerRadius / overstrokeOuterRadius;
745 :
746 : FillInOverstrokeVerts(&verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
747 0 : overstrokeOuterRadius, color, blurRadius);
748 : }
749 :
750 0 : if (kFill_RRectType == args.fType) {
751 0 : SkScalar halfMinDim = 0.5f * SkTMin(bounds.width(), bounds.height());
752 :
753 0 : SkScalar xOffset = 1.0f - outerRadius / halfMinDim;
754 :
755 : FillInOverstrokeVerts(&verts, bounds, outerRadius, halfMinDim, xOffset, halfMinDim,
756 0 : color, blurRadius);
757 : }
758 :
759 0 : const uint16_t* primIndices = rrect_type_to_indices(args.fType);
760 0 : const int primIndexCount = rrect_type_to_index_count(args.fType);
761 0 : for (int i = 0; i < primIndexCount; ++i) {
762 0 : *indices++ = primIndices[i] + currStartVertex;
763 : }
764 :
765 0 : currStartVertex += rrect_type_to_vert_count(args.fType);
766 : }
767 :
768 0 : GrMesh mesh;
769 : mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
770 0 : firstIndex, fVertCount, fIndexCount);
771 0 : target->draw(gp.get(), this->pipeline(), mesh);
772 : }
773 :
774 0 : bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
775 0 : ShadowCircularRRectOp* that = t->cast<ShadowCircularRRectOp>();
776 0 : if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
777 : that->bounds(), caps)) {
778 0 : return false;
779 : }
780 :
781 0 : if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
782 0 : return false;
783 : }
784 :
785 0 : fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
786 0 : this->joinBounds(*that);
787 0 : fVertCount += that->fVertCount;
788 0 : fIndexCount += that->fIndexCount;
789 0 : return true;
790 : }
791 :
792 : struct Geometry {
793 : GrColor fColor;
794 : SkScalar fOuterRadius;
795 : SkScalar fInnerRadius;
796 : SkScalar fBlurRadius;
797 : SkRect fDevBounds;
798 : RRectType fType;
799 : };
800 :
801 : SkSTArray<1, Geometry, true> fGeoData;
802 : SkMatrix fViewMatrixIfUsingLocalCoords;
803 : int fVertCount;
804 : int fIndexCount;
805 :
806 : typedef GrLegacyMeshDrawOp INHERITED;
807 : };
808 :
809 : ///////////////////////////////////////////////////////////////////////////////
810 :
811 0 : std::unique_ptr<GrLegacyMeshDrawOp> make_shadow_circle_op(GrColor color,
812 : const SkMatrix& viewMatrix,
813 : const SkRect& oval,
814 : SkScalar blurRadius,
815 : const SkStrokeRec& stroke,
816 : const GrShaderCaps* shaderCaps) {
817 : // we can only draw circles
818 0 : SkScalar width = oval.width();
819 0 : SkASSERT(SkScalarNearlyEqual(width, oval.height()) && viewMatrix.isSimilarity());
820 0 : SkPoint center = {oval.centerX(), oval.centerY()};
821 : return ShadowCircleOp::Make(color, viewMatrix, center, width / 2.f, blurRadius,
822 0 : GrStyle(stroke, nullptr));
823 : }
824 :
825 0 : static std::unique_ptr<GrLegacyMeshDrawOp> make_shadow_rrect_op(GrColor color,
826 : const SkMatrix& viewMatrix,
827 : const SkRRect& rrect,
828 : SkScalar blurRadius,
829 : const SkStrokeRec& stroke) {
830 0 : SkASSERT(viewMatrix.rectStaysRect());
831 0 : SkASSERT(rrect.isSimple());
832 0 : SkASSERT(!rrect.isOval());
833 :
834 : // Shadow rrect ops only handle simple circular rrects.
835 : // Do any matrix crunching before we reset the draw state for device coords.
836 0 : const SkRect& rrectBounds = rrect.getBounds();
837 : SkRect bounds;
838 0 : viewMatrix.mapRect(&bounds, rrectBounds);
839 :
840 0 : SkVector radii = rrect.getSimpleRadii();
841 0 : SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
842 : viewMatrix[SkMatrix::kMSkewY] * radii.fY);
843 0 : SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
844 : viewMatrix[SkMatrix::kMScaleY] * radii.fY);
845 0 : SkASSERT(SkScalarNearlyEqual(xRadius, yRadius));
846 :
847 0 : SkStrokeRec::Style style = stroke.getStyle();
848 :
849 : // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
850 0 : SkVector scaledStroke = {-1, -1};
851 0 : SkScalar strokeWidth = stroke.getWidth();
852 :
853 : bool isStrokeOnly =
854 0 : SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
855 0 : bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
856 :
857 0 : if (hasStroke) {
858 0 : if (SkStrokeRec::kHairline_Style == style) {
859 0 : scaledStroke.set(1, 1);
860 : } else {
861 0 : scaledStroke.fX = SkScalarAbs(
862 : strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
863 0 : scaledStroke.fY = SkScalarAbs(
864 : strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
865 : }
866 :
867 : // we don't handle anisotropic strokes
868 0 : if (!SkScalarNearlyEqual(scaledStroke.fX, scaledStroke.fY)) {
869 0 : return nullptr;
870 : }
871 : }
872 :
873 : // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
874 : // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
875 : // patch will have fractional coverage. This only matters when the interior is actually filled.
876 : // We could consider falling back to rect rendering here, since a tiny radius is
877 : // indistinguishable from a square corner.
878 0 : if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
879 0 : return nullptr;
880 : }
881 :
882 : return std::unique_ptr<GrLegacyMeshDrawOp>(new ShadowCircularRRectOp(
883 0 : color, viewMatrix, bounds, xRadius, blurRadius, scaledStroke.fX, isStrokeOnly));
884 : }
885 :
886 : namespace GrShadowRRectOp {
887 0 : std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color,
888 : const SkMatrix& viewMatrix,
889 : const SkRRect& rrect,
890 : const SkScalar blurRadius,
891 : const SkStrokeRec& stroke,
892 : const GrShaderCaps* shaderCaps) {
893 0 : if (rrect.isOval()) {
894 : return make_shadow_circle_op(color, viewMatrix, rrect.getBounds(), blurRadius, stroke,
895 0 : shaderCaps);
896 : }
897 :
898 0 : if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
899 0 : return nullptr;
900 : }
901 :
902 0 : return make_shadow_rrect_op(color, viewMatrix, rrect, blurRadius, stroke);
903 : }
904 : }
905 : ///////////////////////////////////////////////////////////////////////////////
906 :
907 : #if GR_TEST_UTILS
908 :
909 0 : DRAW_OP_TEST_DEFINE(ShadowCircleOp) {
910 : do {
911 0 : SkScalar rotate = random->nextSScalar1() * 360.f;
912 0 : SkScalar translateX = random->nextSScalar1() * 1000.f;
913 0 : SkScalar translateY = random->nextSScalar1() * 1000.f;
914 0 : SkScalar scale = random->nextSScalar1() * 100.f;
915 : SkMatrix viewMatrix;
916 0 : viewMatrix.setRotate(rotate);
917 0 : viewMatrix.postTranslate(translateX, translateY);
918 0 : viewMatrix.postScale(scale, scale);
919 0 : GrColor color = GrRandomColor(random);
920 0 : SkRect circle = GrTest::TestSquare(random);
921 0 : SkPoint center = {circle.centerX(), circle.centerY()};
922 0 : SkScalar radius = circle.width() / 2.f;
923 0 : SkStrokeRec stroke = GrTest::TestStrokeRec(random);
924 0 : SkScalar blurRadius = random->nextSScalar1() * 72.f;
925 : std::unique_ptr<GrLegacyMeshDrawOp> op = ShadowCircleOp::Make(
926 0 : color, viewMatrix, center, radius, blurRadius, GrStyle(stroke, nullptr));
927 0 : if (op) {
928 0 : return op;
929 0 : }
930 : } while (true);
931 : }
932 :
933 0 : DRAW_OP_TEST_DEFINE(ShadowRRectOp) {
934 0 : SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
935 0 : GrColor color = GrRandomColor(random);
936 0 : const SkRRect& rrect = GrTest::TestRRectSimple(random);
937 0 : SkScalar blurRadius = random->nextSScalar1() * 72.f;
938 : return make_shadow_rrect_op(color, viewMatrix, rrect, blurRadius,
939 0 : GrTest::TestStrokeRec(random));
940 : }
941 :
942 : #endif
|