Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
StrokeRectOp.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2018 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
9
11#include "src/base/SkRandom.h"
14#include "src/gpu/ResourceKey.h"
26
27using namespace skia_private;
28
30
31namespace {
32
33// This emits line primitives for hairlines, so only support hairlines if allowed by caps. Otherwise
34// we support all hairlines, bevels, and miters, but not round joins. Also, check whether the miter
35// limit makes a miter join effectively beveled. If the miter is effectively beveled, it is only
36// supported when using an AA stroke.
37inline bool allowed_stroke(const GrCaps* caps, const SkStrokeRec& stroke, GrAA aa, bool* isMiter) {
40 if (caps->avoidLineDraws() && stroke.isHairlineStyle()) {
41 return false;
42 }
43 // For hairlines, make bevel and round joins appear the same as mitered ones.
44 if (!stroke.getWidth()) {
45 *isMiter = true;
46 return true;
47 }
48 if (stroke.getJoin() == SkPaint::kBevel_Join) {
49 *isMiter = false;
50 return aa == GrAA::kYes; // bevel only supported with AA
51 }
52 if (stroke.getJoin() == SkPaint::kMiter_Join) {
53 *isMiter = stroke.getMiter() >= SK_ScalarSqrt2;
54 // Supported under non-AA only if it remains mitered
55 return aa == GrAA::kYes || *isMiter;
56 }
57 return false;
58}
59
60
61///////////////////////////////////////////////////////////////////////////////////////////////////
62// Non-AA Stroking
63///////////////////////////////////////////////////////////////////////////////////////////////////
64
65/* create a triangle strip that strokes the specified rect. There are 8
66 unique vertices, but we repeat the last 2 to close up. Alternatively we
67 could use an indices array, and then only send 8 verts, but not sure that
68 would be faster.
69 */
70void init_nonaa_stroke_rect_strip(SkPoint verts[10], const SkRect& rect, SkScalar width) {
71 const SkScalar rad = SkScalarHalf(width);
72
73 verts[0].set(rect.fLeft + rad, rect.fTop + rad);
74 verts[1].set(rect.fLeft - rad, rect.fTop - rad);
75 verts[2].set(rect.fRight - rad, rect.fTop + rad);
76 verts[3].set(rect.fRight + rad, rect.fTop - rad);
77 verts[4].set(rect.fRight - rad, rect.fBottom - rad);
78 verts[5].set(rect.fRight + rad, rect.fBottom + rad);
79 verts[6].set(rect.fLeft + rad, rect.fBottom - rad);
80 verts[7].set(rect.fLeft - rad, rect.fBottom + rad);
81 verts[8] = verts[0];
82 verts[9] = verts[1];
83
84 // TODO: we should be catching this higher up the call stack and just draw a single
85 // non-AA rect
86 if (2*rad >= rect.width()) {
87 verts[0].fX = verts[2].fX = verts[4].fX = verts[6].fX = verts[8].fX = rect.centerX();
88 }
89 if (2*rad >= rect.height()) {
90 verts[0].fY = verts[2].fY = verts[4].fY = verts[6].fY = verts[8].fY = rect.centerY();
91 }
92}
93
94class NonAAStrokeRectOp final : public GrMeshDrawOp {
95private:
96 using Helper = GrSimpleMeshDrawOpHelper;
97
98public:
100
101 const char* name() const override { return "NonAAStrokeRectOp"; }
102
103 void visitProxies(const GrVisitProxyFunc& func) const override {
104 if (fProgramInfo) {
105 fProgramInfo->visitFPProxies(func);
106 } else {
107 fHelper.visitProxies(func);
108 }
109 }
110
111 static GrOp::Owner Make(GrRecordingContext* context,
112 GrPaint&& paint,
113 const SkMatrix& viewMatrix,
114 const SkRect& rect,
115 const SkStrokeRec& stroke,
116 GrAAType aaType) {
117 bool isMiter;
118 if (!allowed_stroke(context->priv().caps(), stroke, GrAA::kNo, &isMiter)) {
119 return nullptr;
120 }
122 // Depending on sub-pixel coordinates and the particular GPU, we may lose a corner of
123 // hairline rects. We jam all the vertices to pixel centers to avoid this, but not
124 // when MSAA is enabled because it can cause ugly artifacts.
125 if (stroke.getStyle() == SkStrokeRec::kHairline_Style && aaType != GrAAType::kMSAA) {
127 }
128 return Helper::FactoryHelper<NonAAStrokeRectOp>(context, std::move(paint), inputFlags,
129 viewMatrix, rect,
130 stroke, aaType);
131 }
132
133 NonAAStrokeRectOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
134 Helper::InputFlags inputFlags, const SkMatrix& viewMatrix, const SkRect& rect,
135 const SkStrokeRec& stroke, GrAAType aaType)
136 : INHERITED(ClassID())
137 , fHelper(processorSet, aaType, inputFlags) {
138 fColor = color;
139 fViewMatrix = viewMatrix;
140 fRect = rect;
141 // Sort the rect for hairlines
142 fRect.sort();
143 fStrokeWidth = stroke.getWidth();
144
145 SkScalar rad = SkScalarHalf(fStrokeWidth);
146 SkRect bounds = rect;
147 bounds.outset(rad, rad);
148
149 // If our caller snaps to pixel centers then we have to round out the bounds
151 SkASSERT(!fStrokeWidth || aaType == GrAAType::kNone);
152 viewMatrix.mapRect(&bounds);
153 // We want to be consistent with how we snap non-aa lines. To match what we do in
154 // GrGLSLVertexShaderBuilder, we first floor all the vertex values and then add half a
155 // pixel to force us to pixel centers.
156 bounds.setLTRB(SkScalarFloorToScalar(bounds.fLeft),
157 SkScalarFloorToScalar(bounds.fTop),
158 SkScalarFloorToScalar(bounds.fRight),
159 SkScalarFloorToScalar(bounds.fBottom));
160 bounds.offset(0.5f, 0.5f);
161 this->setBounds(bounds, HasAABloat::kNo, IsHairline::kNo);
162 } else {
163 HasAABloat aaBloat = (aaType == GrAAType::kNone) ? HasAABloat ::kNo : HasAABloat::kYes;
164 this->setTransformedBounds(bounds, fViewMatrix, aaBloat,
165 fStrokeWidth ? IsHairline::kNo : IsHairline::kYes);
166 }
167 }
168
169 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
170
171 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
172 GrClampType clampType) override {
173 // This Op uses uniform (not vertex) color, so doesn't need to track wide color.
174 return fHelper.finalizeProcessors(caps, clip, clampType, GrProcessorAnalysisCoverage::kNone,
175 &fColor, nullptr);
176 }
177
178private:
179 GrProgramInfo* programInfo() override { return fProgramInfo; }
180
181 void onCreateProgramInfo(const GrCaps* caps,
182 SkArenaAlloc* arena,
183 const GrSurfaceProxyView& writeView,
184 bool usesMSAASurface,
186 const GrDstProxyView& dstProxyView,
187 GrXferBarrierFlags renderPassXferBarriers,
188 GrLoadOp colorLoadOp) override {
190 {
191 using namespace GrDefaultGeoProcFactory;
192 Color color(fColor);
193 LocalCoords::Type localCoordsType = fHelper.usesLocalCoords()
194 ? LocalCoords::kUsePosition_Type
195 : LocalCoords::kUnused_Type;
196 gp = GrDefaultGeoProcFactory::Make(arena, color, Coverage::kSolid_Type, localCoordsType,
198 }
199
200 GrPrimitiveType primType = (fStrokeWidth > 0) ? GrPrimitiveType::kTriangleStrip
202
203 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, usesMSAASurface,
204 std::move(clip), dstProxyView, gp, primType,
205 renderPassXferBarriers, colorLoadOp);
206 }
207
208 void onPrepareDraws(GrMeshDrawTarget* target) override {
209 if (!fProgramInfo) {
210 this->createProgramInfo(target);
211 }
212
213 size_t kVertexStride = fProgramInfo->geomProc().vertexStride();
214 int vertexCount = kVertsPerHairlineRect;
215 if (fStrokeWidth > 0) {
216 vertexCount = kVertsPerStrokeRect;
217 }
218
219 sk_sp<const GrBuffer> vertexBuffer;
220 int firstVertex;
221
222 void* verts =
223 target->makeVertexSpace(kVertexStride, vertexCount, &vertexBuffer, &firstVertex);
224
225 if (!verts) {
226 SkDebugf("Could not allocate vertices\n");
227 return;
228 }
229
230 SkPoint* vertex = reinterpret_cast<SkPoint*>(verts);
231
232 if (fStrokeWidth > 0) {
233 init_nonaa_stroke_rect_strip(vertex, fRect, fStrokeWidth);
234 } else {
235 // hairline
236 vertex[0].set(fRect.fLeft, fRect.fTop);
237 vertex[1].set(fRect.fRight, fRect.fTop);
238 vertex[2].set(fRect.fRight, fRect.fBottom);
239 vertex[3].set(fRect.fLeft, fRect.fBottom);
240 vertex[4].set(fRect.fLeft, fRect.fTop);
241 }
242
243 fMesh = target->allocMesh();
244 fMesh->set(std::move(vertexBuffer), vertexCount, firstVertex);
245 }
246
247 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
248 if (!fMesh) {
249 return;
250 }
251
252 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
253 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
254 flushState->drawMesh(*fMesh);
255 }
256
257#if defined(GR_TEST_UTILS)
258 SkString onDumpInfo() const override {
259 return SkStringPrintf("Color: 0x%08x, Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
260 "StrokeWidth: %.2f\n%s",
261 fColor.toBytes_RGBA(), fRect.fLeft, fRect.fTop, fRect.fRight,
262 fRect.fBottom, fStrokeWidth, fHelper.dumpInfo().c_str());
263 }
264#endif
265
266 // TODO: override onCombineIfPossible
267
268 Helper fHelper;
269 SkPMColor4f fColor;
272 SkScalar fStrokeWidth;
273 GrSimpleMesh* fMesh = nullptr;
274 GrProgramInfo* fProgramInfo = nullptr;
275
276 const static int kVertsPerHairlineRect = 5;
277 const static int kVertsPerStrokeRect = 10;
278
279 using INHERITED = GrMeshDrawOp;
280};
281
282///////////////////////////////////////////////////////////////////////////////////////////////////
283// AA Stroking
284///////////////////////////////////////////////////////////////////////////////////////////////////
285
286SKGPU_DECLARE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
287SKGPU_DECLARE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
288
289bool stroke_dev_half_size_supported(SkVector devHalfStrokeSize) {
290 // Since the horizontal and vertical strokes share internal corners, the coverage value at that
291 // corner needs to be equal for the horizontal and vertical strokes both.
292 //
293 // The inner coverage values will be equal if the horizontal and vertical stroke widths are
294 // equal (in which case innerCoverage is same for all sides of the rects) or if the horizontal
295 // and vertical stroke widths are both greater than 1 (in which case innerCoverage will always
296 // be 1). In actuality we allow them to be nearly-equal since differing by < 1/1000 will not be
297 // visually detectable when the shape is already less than 1px in thickness.
298 return SkScalarNearlyEqual(devHalfStrokeSize.fX, devHalfStrokeSize.fY) ||
299 std::min(devHalfStrokeSize.fX, devHalfStrokeSize.fY) >= .5f;
300}
301
302bool compute_aa_rects(const GrCaps& caps,
303 SkRect* devOutside,
304 SkRect* devOutsideAssist,
305 SkRect* devInside,
306 bool* isDegenerate,
307 const SkMatrix& viewMatrix,
308 const SkRect& rect,
310 bool miterStroke,
311 SkVector* devHalfStrokeSize) {
312 SkVector devStrokeSize;
313 if (strokeWidth > 0) {
314 devStrokeSize.set(strokeWidth, strokeWidth);
315 viewMatrix.mapVectors(&devStrokeSize, 1);
316 devStrokeSize.setAbs(devStrokeSize);
317 } else {
318 devStrokeSize.set(SK_Scalar1, SK_Scalar1);
319 }
320
321 const SkScalar dx = devStrokeSize.fX;
322 const SkScalar dy = devStrokeSize.fY;
323 const SkScalar rx = SkScalarHalf(dx);
324 const SkScalar ry = SkScalarHalf(dy);
325
326 devHalfStrokeSize->fX = rx;
327 devHalfStrokeSize->fY = ry;
328
329 SkRect devRect;
330 viewMatrix.mapRect(&devRect, rect);
331
332 // Clip our draw rect 1 full stroke width plus bloat outside the viewport. This avoids
333 // interpolation precision issues with very large coordinates.
334 const float m = caps.maxRenderTargetSize();
335 const SkRect visibilityBounds = SkRect::MakeWH(m, m).makeOutset(dx + 1, dy + 1);
336 if (!devRect.intersect(visibilityBounds)) {
337 return false;
338 }
339
340 *devOutside = devRect;
341 *devOutsideAssist = devRect;
342 *devInside = devRect;
343
344 devOutside->outset(rx, ry);
345 devInside->inset(rx, ry);
346
347 // If we have a degenerate stroking rect(ie the stroke is larger than inner rect) then we
348 // make a degenerate inside rect to avoid double hitting. We will also jam all of the points
349 // together when we render these rects.
350 SkScalar spare;
351 {
352 SkScalar w = devRect.width() - dx;
353 SkScalar h = devRect.height() - dy;
354 spare = std::min(w, h);
355 }
356
357 *isDegenerate = spare <= 0;
358 if (*isDegenerate) {
359 devInside->fLeft = devInside->fRight = devRect.centerX();
360 devInside->fTop = devInside->fBottom = devRect.centerY();
361 }
362
363 // For bevel-stroke, use 2 SkRect instances(devOutside and devOutsideAssist)
364 // to draw the outside of the octagon. Because there are 8 vertices on the outer
365 // edge, while vertex number of inner edge is 4, the same as miter-stroke.
366 if (!miterStroke) {
367 devOutside->inset(0, ry);
368 devOutsideAssist->outset(0, ry);
369 }
370
371 return true;
372}
373
374GrGeometryProcessor* create_aa_stroke_rect_gp(SkArenaAlloc* arena,
375 bool usesMSAASurface,
376 bool tweakAlphaForCoverage,
377 const SkMatrix& viewMatrix,
378 bool usesLocalCoords,
379 bool wideColor) {
380 using namespace GrDefaultGeoProcFactory;
381
382 // When MSAA is enabled, we have to extend our AA bloats and interpolate coverage values outside
383 // 0..1. We tell the gp in this case that coverage is an unclamped attribute so it will call
384 // saturate(coverage) in the fragment shader.
385 Coverage::Type coverageType = usesMSAASurface ? Coverage::kAttributeUnclamped_Type
386 : (!tweakAlphaForCoverage ? Coverage::kAttribute_Type
387 : Coverage::kSolid_Type);
388 LocalCoords::Type localCoordsType =
389 usesLocalCoords ? LocalCoords::kUsePosition_Type : LocalCoords::kUnused_Type;
391 wideColor ? Color::kPremulWideColorAttribute_Type: Color::kPremulGrColorAttribute_Type;
392
393 return MakeForDeviceSpace(arena, colorType, coverageType, localCoordsType, viewMatrix);
394}
395
396class AAStrokeRectOp final : public GrMeshDrawOp {
397private:
398 using Helper = GrSimpleMeshDrawOpHelper;
399
400public:
402
403 // TODO support AA rotated stroke rects by copying around view matrices
404 struct RectInfo {
405 SkPMColor4f fColor;
411 };
412
413 static GrOp::Owner Make(GrRecordingContext* context,
414 GrPaint&& paint,
415 const SkMatrix& viewMatrix,
416 const SkRect& devOutside,
417 const SkRect& devInside,
418 const SkVector& devHalfStrokeSize) {
419 if (!viewMatrix.rectStaysRect()) {
420 // The AA op only supports axis-aligned rectangles
421 return nullptr;
422 }
423 if (!stroke_dev_half_size_supported(devHalfStrokeSize)) {
424 return nullptr;
425 }
426 return Helper::FactoryHelper<AAStrokeRectOp>(context, std::move(paint), viewMatrix,
427 devOutside, devInside, devHalfStrokeSize);
428 }
429
430 AAStrokeRectOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
431 const SkMatrix& viewMatrix, const SkRect& devOutside, const SkRect& devInside,
432 const SkVector& devHalfStrokeSize)
433 : INHERITED(ClassID())
434 , fHelper(processorSet, GrAAType::kCoverage)
435 , fViewMatrix(viewMatrix) {
436 SkASSERT(!devOutside.isEmpty());
437 SkASSERT(!devInside.isEmpty());
438
439 fRects.emplace_back(RectInfo{color, devOutside, devOutside, devInside, devHalfStrokeSize, false});
440 this->setBounds(devOutside, HasAABloat::kYes, IsHairline::kNo);
441 fMiterStroke = true;
442 }
443
444 static GrOp::Owner Make(GrRecordingContext* context,
445 GrPaint&& paint,
446 const SkMatrix& viewMatrix,
447 const SkRect& rect,
448 const SkStrokeRec& stroke) {
449 if (!viewMatrix.rectStaysRect()) {
450 // The AA op only supports axis-aligned rectangles
451 return nullptr;
452 }
453 bool isMiter;
454 if (!allowed_stroke(context->priv().caps(), stroke, GrAA::kYes, &isMiter)) {
455 return nullptr;
456 }
457 RectInfo info;
458 if (!compute_aa_rects(*context->priv().caps(),
459 &info.fDevOutside,
460 &info.fDevOutsideAssist,
461 &info.fDevInside,
462 &info.fDegenerate,
463 viewMatrix,
464 rect,
465 stroke.getWidth(),
466 isMiter,
467 &info.fDevHalfStrokeSize)) {
468 return nullptr;
469 }
470 if (!stroke_dev_half_size_supported(info.fDevHalfStrokeSize)) {
471 return nullptr;
472 }
473 return Helper::FactoryHelper<AAStrokeRectOp>(context, std::move(paint), viewMatrix, info,
474 isMiter);
475 }
476
477 AAStrokeRectOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
478 const SkMatrix& viewMatrix, const RectInfo& infoExceptColor, bool isMiter)
479 : INHERITED(ClassID())
480 , fHelper(processorSet, GrAAType::kCoverage)
481 , fViewMatrix(viewMatrix) {
482 fMiterStroke = isMiter;
483 RectInfo& info = fRects.push_back(infoExceptColor);
484 info.fColor = color;
485 if (isMiter) {
486 this->setBounds(info.fDevOutside, HasAABloat::kYes, IsHairline::kNo);
487 } else {
488 // The outer polygon of the bevel stroke is an octagon specified by the points of a
489 // pair of overlapping rectangles where one is wide and the other is narrow.
490 SkRect bounds = info.fDevOutside;
491 bounds.joinPossiblyEmptyRect(info.fDevOutsideAssist);
492 this->setBounds(bounds, HasAABloat::kYes, IsHairline::kNo);
493 }
494 }
495
496 const char* name() const override { return "AAStrokeRect"; }
497
498 void visitProxies(const GrVisitProxyFunc& func) const override {
499 if (fProgramInfo) {
500 fProgramInfo->visitFPProxies(func);
501 } else {
502 fHelper.visitProxies(func);
503 }
504 }
505
506 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
507
508 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
509 GrClampType clampType) override {
510 return fHelper.finalizeProcessors(caps, clip, clampType,
512 &fRects.back().fColor, &fWideColor);
513 }
514
515private:
516 GrProgramInfo* programInfo() override { return fProgramInfo; }
517
518 bool compatibleWithCoverageAsAlpha(bool usesMSAASurface) const {
519 // When MSAA is enabled, we have to extend our AA bloats and interpolate coverage values
520 // outside 0..1. This makes us incompatible with coverage as alpha.
521 return !usesMSAASurface && fHelper.compatibleWithCoverageAsAlpha();
522 }
523
524 void onCreateProgramInfo(const GrCaps*,
526 const GrSurfaceProxyView& writeView,
527 bool usesMSAASurface,
529 const GrDstProxyView&,
530 GrXferBarrierFlags renderPassXferBarriers,
531 GrLoadOp colorLoadOp) override;
532
533 void onPrepareDraws(GrMeshDrawTarget*) override;
534 void onExecute(GrOpFlushState*, const SkRect& chainBounds) override;
535
536#if defined(GR_TEST_UTILS)
537 SkString onDumpInfo() const override {
538 SkString string;
539 for (const auto& info : fRects) {
540 string.appendf(
541 "Color: 0x%08x, ORect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
542 "AssistORect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
543 "IRect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], Degen: %d",
544 info.fColor.toBytes_RGBA(), info.fDevOutside.fLeft, info.fDevOutside.fTop,
545 info.fDevOutside.fRight, info.fDevOutside.fBottom, info.fDevOutsideAssist.fLeft,
546 info.fDevOutsideAssist.fTop, info.fDevOutsideAssist.fRight,
547 info.fDevOutsideAssist.fBottom, info.fDevInside.fLeft, info.fDevInside.fTop,
548 info.fDevInside.fRight, info.fDevInside.fBottom, info.fDegenerate);
549 }
550 string += fHelper.dumpInfo();
551 return string;
552 }
553#endif
554
555 static const int kMiterIndexCnt = 3 * 24;
556 static const int kMiterVertexCnt = 16;
557 static const int kNumMiterRectsInIndexBuffer = 256;
558
559 static const int kBevelIndexCnt = 48 + 36 + 24;
560 static const int kBevelVertexCnt = 24;
561 static const int kNumBevelRectsInIndexBuffer = 256;
562
564
565 const SkMatrix& viewMatrix() const { return fViewMatrix; }
566 bool miterStroke() const { return fMiterStroke; }
567
568 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps&) override;
569
570 void generateAAStrokeRectGeometry(VertexWriter& vertices,
571 const SkPMColor4f& color,
572 bool wideColor,
573 const SkRect& devOutside,
574 const SkRect& devOutsideAssist,
575 const SkRect& devInside,
576 bool miterStroke,
577 bool degenerate,
578 const SkVector& devHalfStrokeSize,
579 bool usesMSAASurface) const;
580
581 Helper fHelper;
584 GrSimpleMesh* fMesh = nullptr;
585 GrProgramInfo* fProgramInfo = nullptr;
586 bool fMiterStroke;
587 bool fWideColor;
588
589 using INHERITED = GrMeshDrawOp;
590};
591
592void AAStrokeRectOp::onCreateProgramInfo(const GrCaps* caps,
593 SkArenaAlloc* arena,
594 const GrSurfaceProxyView& writeView,
595 bool usesMSAASurface,
596 GrAppliedClip&& appliedClip,
597 const GrDstProxyView& dstProxyView,
598 GrXferBarrierFlags renderPassXferBarriers,
599 GrLoadOp colorLoadOp) {
600
601 GrGeometryProcessor* gp = create_aa_stroke_rect_gp(
602 arena,
603 usesMSAASurface,
604 this->compatibleWithCoverageAsAlpha(usesMSAASurface),
605 this->viewMatrix(),
606 fHelper.usesLocalCoords(),
607 fWideColor);
608 if (!gp) {
609 SkDebugf("Couldn't create GrGeometryProcessor\n");
610 return;
611 }
612
613 fProgramInfo = fHelper.createProgramInfo(caps,
614 arena,
615 writeView,
616 usesMSAASurface,
617 std::move(appliedClip),
618 dstProxyView,
619 gp,
621 renderPassXferBarriers,
622 colorLoadOp);
623}
624
625void AAStrokeRectOp::onPrepareDraws(GrMeshDrawTarget* target) {
626
627 if (!fProgramInfo) {
628 this->createProgramInfo(target);
629 if (!fProgramInfo) {
630 return;
631 }
632 }
633
634 int innerVertexNum = 4;
635 int outerVertexNum = this->miterStroke() ? 4 : 8;
636 int verticesPerInstance = (outerVertexNum + innerVertexNum) * 2;
637 int indicesPerInstance = this->miterStroke() ? kMiterIndexCnt : kBevelIndexCnt;
638 int instanceCount = fRects.size();
639 int maxQuads = this->miterStroke() ? kNumMiterRectsInIndexBuffer : kNumBevelRectsInIndexBuffer;
640
641 sk_sp<const GrGpuBuffer> indexBuffer =
642 GetIndexBuffer(target->resourceProvider(), this->miterStroke());
643 if (!indexBuffer) {
644 SkDebugf("Could not allocate indices\n");
645 return;
646 }
647 PatternHelper helper(target, GrPrimitiveType::kTriangles,
648 fProgramInfo->geomProc().vertexStride(), std::move(indexBuffer),
649 verticesPerInstance, indicesPerInstance, instanceCount, maxQuads);
650 VertexWriter vertices{ helper.vertices() };
651 if (!vertices) {
652 SkDebugf("Could not allocate vertices\n");
653 return;
654 }
655
656 for (int i = 0; i < instanceCount; i++) {
657 const RectInfo& info = fRects[i];
658 this->generateAAStrokeRectGeometry(vertices,
659 info.fColor,
660 fWideColor,
661 info.fDevOutside,
662 info.fDevOutsideAssist,
663 info.fDevInside,
664 fMiterStroke,
665 info.fDegenerate,
666 info.fDevHalfStrokeSize,
667 target->usesMSAASurface());
668 }
669 fMesh = helper.mesh();
670}
671
672void AAStrokeRectOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
673 if (!fProgramInfo || !fMesh) {
674 return;
675 }
676
677 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
678 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
679 flushState->drawMesh(*fMesh);
680}
681
682sk_sp<const GrGpuBuffer> AAStrokeRectOp::GetIndexBuffer(GrResourceProvider* resourceProvider,
683 bool miterStroke) {
684 if (miterStroke) {
685 // clang-format off
686 static const uint16_t gMiterIndices[] = {
687 0 + 0, 1 + 0, 5 + 0, 5 + 0, 4 + 0, 0 + 0,
688 1 + 0, 2 + 0, 6 + 0, 6 + 0, 5 + 0, 1 + 0,
689 2 + 0, 3 + 0, 7 + 0, 7 + 0, 6 + 0, 2 + 0,
690 3 + 0, 0 + 0, 4 + 0, 4 + 0, 7 + 0, 3 + 0,
691
692 0 + 4, 1 + 4, 5 + 4, 5 + 4, 4 + 4, 0 + 4,
693 1 + 4, 2 + 4, 6 + 4, 6 + 4, 5 + 4, 1 + 4,
694 2 + 4, 3 + 4, 7 + 4, 7 + 4, 6 + 4, 2 + 4,
695 3 + 4, 0 + 4, 4 + 4, 4 + 4, 7 + 4, 3 + 4,
696
697 0 + 8, 1 + 8, 5 + 8, 5 + 8, 4 + 8, 0 + 8,
698 1 + 8, 2 + 8, 6 + 8, 6 + 8, 5 + 8, 1 + 8,
699 2 + 8, 3 + 8, 7 + 8, 7 + 8, 6 + 8, 2 + 8,
700 3 + 8, 0 + 8, 4 + 8, 4 + 8, 7 + 8, 3 + 8,
701 };
702 // clang-format on
703 static_assert(std::size(gMiterIndices) == kMiterIndexCnt);
704 SKGPU_DEFINE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
705 return resourceProvider->findOrCreatePatternedIndexBuffer(
706 gMiterIndices, kMiterIndexCnt, kNumMiterRectsInIndexBuffer, kMiterVertexCnt,
707 gMiterIndexBufferKey);
708 } else {
709 /**
710 * As in miter-stroke, index = a + b, and a is the current index, b is the shift
711 * from the first index. The index layout:
712 * outer AA line: 0~3, 4~7
713 * outer edge: 8~11, 12~15
714 * inner edge: 16~19
715 * inner AA line: 20~23
716 * Following comes a bevel-stroke rect and its indices:
717 *
718 * 4 7
719 * *********************************
720 * * ______________________________ *
721 * * / 12 15 \ *
722 * * / \ *
723 * 0 * |8 16_____________________19 11 | * 3
724 * * | | | | *
725 * * | | **************** | | *
726 * * | | * 20 23 * | | *
727 * * | | * * | | *
728 * * | | * 21 22 * | | *
729 * * | | **************** | | *
730 * * | |____________________| | *
731 * 1 * |9 17 18 10| * 2
732 * * \ / *
733 * * \13 __________________________14/ *
734 * * *
735 * **********************************
736 * 5 6
737 */
738 // clang-format off
739 static const uint16_t gBevelIndices[] = {
740 // Draw outer AA, from outer AA line to outer edge, shift is 0.
741 0 + 0, 1 + 0, 9 + 0, 9 + 0, 8 + 0, 0 + 0,
742 1 + 0, 5 + 0, 13 + 0, 13 + 0, 9 + 0, 1 + 0,
743 5 + 0, 6 + 0, 14 + 0, 14 + 0, 13 + 0, 5 + 0,
744 6 + 0, 2 + 0, 10 + 0, 10 + 0, 14 + 0, 6 + 0,
745 2 + 0, 3 + 0, 11 + 0, 11 + 0, 10 + 0, 2 + 0,
746 3 + 0, 7 + 0, 15 + 0, 15 + 0, 11 + 0, 3 + 0,
747 7 + 0, 4 + 0, 12 + 0, 12 + 0, 15 + 0, 7 + 0,
748 4 + 0, 0 + 0, 8 + 0, 8 + 0, 12 + 0, 4 + 0,
749
750 // Draw the stroke, from outer edge to inner edge, shift is 8.
751 0 + 8, 1 + 8, 9 + 8, 9 + 8, 8 + 8, 0 + 8,
752 1 + 8, 5 + 8, 9 + 8,
753 5 + 8, 6 + 8, 10 + 8, 10 + 8, 9 + 8, 5 + 8,
754 6 + 8, 2 + 8, 10 + 8,
755 2 + 8, 3 + 8, 11 + 8, 11 + 8, 10 + 8, 2 + 8,
756 3 + 8, 7 + 8, 11 + 8,
757 7 + 8, 4 + 8, 8 + 8, 8 + 8, 11 + 8, 7 + 8,
758 4 + 8, 0 + 8, 8 + 8,
759
760 // Draw the inner AA, from inner edge to inner AA line, shift is 16.
761 0 + 16, 1 + 16, 5 + 16, 5 + 16, 4 + 16, 0 + 16,
762 1 + 16, 2 + 16, 6 + 16, 6 + 16, 5 + 16, 1 + 16,
763 2 + 16, 3 + 16, 7 + 16, 7 + 16, 6 + 16, 2 + 16,
764 3 + 16, 0 + 16, 4 + 16, 4 + 16, 7 + 16, 3 + 16,
765 };
766 // clang-format on
767 static_assert(std::size(gBevelIndices) == kBevelIndexCnt);
768
769 SKGPU_DEFINE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
770 return resourceProvider->findOrCreatePatternedIndexBuffer(
771 gBevelIndices, kBevelIndexCnt, kNumBevelRectsInIndexBuffer, kBevelVertexCnt,
772 gBevelIndexBufferKey);
773 }
774}
775
776GrOp::CombineResult AAStrokeRectOp::onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps)
777{
778 AAStrokeRectOp* that = t->cast<AAStrokeRectOp>();
779
780 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
781 return CombineResult::kCannotCombine;
782 }
783
784 // TODO combine across miterstroke changes
785 if (this->miterStroke() != that->miterStroke()) {
786 return CombineResult::kCannotCombine;
787 }
788
789 // We apply the viewmatrix to the rect points on the cpu. However, if the pipeline uses
790 // local coords then we won't be able to combine. TODO: Upload local coords as an attribute.
791 if (fHelper.usesLocalCoords() &&
792 !SkMatrixPriv::CheapEqual(this->viewMatrix(), that->viewMatrix()))
793 {
794 return CombineResult::kCannotCombine;
795 }
796
797 fRects.push_back_n(that->fRects.size(), that->fRects.begin());
798 fWideColor |= that->fWideColor;
799 return CombineResult::kMerged;
800}
801
802void AAStrokeRectOp::generateAAStrokeRectGeometry(VertexWriter& vertices,
803 const SkPMColor4f& color,
804 bool wideColor,
805 const SkRect& devOutside,
806 const SkRect& devOutsideAssist,
807 const SkRect& devInside,
808 bool miterStroke,
809 bool degenerate,
810 const SkVector& devHalfStrokeSize,
811 bool usesMSAASurface) const {
812 // We create vertices for four nested rectangles. There are two ramps from 0 to full
813 // coverage, one on the exterior of the stroke and the other on the interior.
814
815 // The following code only works if either devStrokeSize's fX and fY are
816 // equal (in which case innerCoverage is same for all sides of the rects) or
817 // if devStrokeSize's fX and fY are both greater than 1.0 (in which case
818 // innerCoverage will always be 1).
819 SkASSERT(stroke_dev_half_size_supported(devHalfStrokeSize));
820
821 auto inset_fan = [](const SkRect& r, SkScalar dx, SkScalar dy) {
822 return VertexWriter::TriFanFromRect(r.makeInset(dx, dy));
823 };
824
825 bool tweakAlphaForCoverage = this->compatibleWithCoverageAsAlpha(usesMSAASurface);
826
827 auto maybe_coverage = [tweakAlphaForCoverage](float coverage) {
828 return VertexWriter::If(!tweakAlphaForCoverage, coverage);
829 };
830
831 // How much do we inset toward the inside of the strokes?
832 float inset = std::min(0.5f, std::min(devHalfStrokeSize.fX, devHalfStrokeSize.fY));
833 float innerCoverage = 1;
834 if (inset < 0.5f) {
835 // Stroke is subpixel, so reduce the coverage to simulate the narrower strokes.
836 innerCoverage = 2 * inset / (inset + .5f);
837 }
838
839 // How much do we outset away from the outside of the strokes?
840 // We always want to keep the AA picture frame one pixel wide.
841 float outset = 1 - inset;
842 float outerCoverage = 0;
843
844 // How much do we outset away from the interior side of the stroke (toward the center)?
845 float interiorOutset = outset;
846 float interiorCoverage = outerCoverage;
847
848 if (usesMSAASurface) {
849 // Since we're using MSAA, extend our outsets to ensure any pixel with partial coverage has
850 // a full sample mask.
851 constexpr float msaaExtraBloat = SK_ScalarSqrt2 - .5f;
852 outset += msaaExtraBloat;
853 outerCoverage -= msaaExtraBloat;
854
855 float insetExtraBloat =
856 std::min(inset + msaaExtraBloat,
857 std::min(devHalfStrokeSize.fX, devHalfStrokeSize.fY)) - inset;
858 inset += insetExtraBloat;
859 innerCoverage += insetExtraBloat;
860
861 float interiorExtraBloat =
862 std::min(interiorOutset + msaaExtraBloat,
863 std::min(devInside.width(), devInside.height()) / 2) - interiorOutset;
864 interiorOutset += interiorExtraBloat;
865 interiorCoverage -= interiorExtraBloat;
866 }
867
868 VertexColor innerColor(tweakAlphaForCoverage ? color * innerCoverage : color, wideColor);
869 VertexColor outerColor(tweakAlphaForCoverage ? SK_PMColor4fTRANSPARENT : color, wideColor);
870
871 // Exterior outset rect (away from stroke).
872 vertices.writeQuad(inset_fan(devOutside, -outset, -outset),
873 outerColor,
874 maybe_coverage(outerCoverage));
875
876 if (!miterStroke) {
877 // Second exterior outset.
878 vertices.writeQuad(inset_fan(devOutsideAssist, -outset, -outset),
879 outerColor,
880 maybe_coverage(outerCoverage));
881 }
882
883 // Exterior inset rect (toward stroke).
884 vertices.writeQuad(inset_fan(devOutside, inset, inset),
885 innerColor,
886 maybe_coverage(innerCoverage));
887
888 if (!miterStroke) {
889 // Second exterior inset.
890 vertices.writeQuad(inset_fan(devOutsideAssist, inset, inset),
891 innerColor,
892 maybe_coverage(innerCoverage));
893 }
894
895 if (!degenerate) {
896 // Interior inset rect (toward stroke).
897 vertices.writeQuad(inset_fan(devInside, -inset, -inset),
898 innerColor,
899 maybe_coverage(innerCoverage));
900
901 // Interior outset rect (away from stroke, toward center of rect).
902 SkRect interiorAABoundary = devInside.makeInset(interiorOutset, interiorOutset);
903 float coverageBackset = 0; // Adds back coverage when the interior AA edges cross.
904 if (interiorAABoundary.fLeft > interiorAABoundary.fRight) {
905 coverageBackset =
906 (interiorAABoundary.fLeft - interiorAABoundary.fRight) / (interiorOutset * 2);
907 interiorAABoundary.fLeft = interiorAABoundary.fRight = interiorAABoundary.centerX();
908 }
909 if (interiorAABoundary.fTop > interiorAABoundary.fBottom) {
910 coverageBackset = std::max(
911 (interiorAABoundary.fTop - interiorAABoundary.fBottom) / (interiorOutset * 2),
912 coverageBackset);
913 interiorAABoundary.fTop = interiorAABoundary.fBottom = interiorAABoundary.centerY();
914 }
915 if (coverageBackset > 0) {
916 // The interior edges crossed. Lerp back toward innerCoverage, which is what this op
917 // will draw in the degenerate case. This gives a smooth transition into the degenerate
918 // case.
919 interiorCoverage += interiorCoverage * (1 - coverageBackset) +
920 innerCoverage * coverageBackset;
921 }
922 VertexColor interiorColor(tweakAlphaForCoverage ? color * interiorCoverage : color,
923 wideColor);
924 vertices.writeQuad(VertexWriter::TriFanFromRect(interiorAABoundary),
925 interiorColor,
926 maybe_coverage(interiorCoverage));
927 } else {
928 // When the interior rect has become degenerate we smoosh to a single point
929 SkASSERT(devInside.fLeft == devInside.fRight && devInside.fTop == devInside.fBottom);
930
931 vertices.writeQuad(VertexWriter::TriFanFromRect(devInside),
932 innerColor,
933 maybe_coverage(innerCoverage));
934
935 // ... unless we are degenerate, in which case we must apply the scaled coverage
936 vertices.writeQuad(VertexWriter::TriFanFromRect(devInside),
937 innerColor,
938 maybe_coverage(innerCoverage));
939 }
940}
941
942} // anonymous namespace
943
945 GrPaint&& paint,
946 GrAAType aaType,
947 const SkMatrix& viewMatrix,
948 const SkRect& rect,
949 const SkStrokeRec& stroke) {
950 SkASSERT(!context->priv().caps()->reducedShaderMode());
951 if (aaType == GrAAType::kCoverage) {
952 return AAStrokeRectOp::Make(context, std::move(paint), viewMatrix, rect, stroke);
953 } else {
954 return NonAAStrokeRectOp::Make(context, std::move(paint), viewMatrix, rect, stroke, aaType);
955 }
956}
957
959 GrPaint&& paint,
960 const SkMatrix& viewMatrix,
961 const SkRect rects[2]) {
962 SkASSERT(viewMatrix.rectStaysRect());
963 SkASSERT(!rects[0].isEmpty() && !rects[1].isEmpty());
964
965 SkRect devOutside = viewMatrix.mapRect(rects[0]);
966 SkRect devInside = viewMatrix.mapRect(rects[1]);
967 float dx = devOutside.fRight - devInside.fRight;
968 float dy = devOutside.fBottom - devInside.fBottom;
969
970 // Clips our draw rects 1 full pixel outside the viewport. This avoids interpolation precision
971 // issues with very large coordinates.
972 const float m = context->priv().caps()->maxRenderTargetSize();
973 const SkRect visibilityBounds = SkRect::MakeWH(m, m).makeOutset(1, 1);
974
975 if (!devOutside.intersect(visibilityBounds.makeOutset(dx, dy))) {
976 return nullptr;
977 }
978
979 if (devInside.isEmpty() || !devInside.intersect(visibilityBounds)) {
980 if (devOutside.isEmpty()) {
981 return nullptr;
982 }
983 DrawQuad quad{GrQuad::MakeFromRect(rects[0], viewMatrix), GrQuad(rects[0]),
985 return ganesh::FillRectOp::Make(context, std::move(paint), GrAAType::kCoverage, &quad);
986 }
987
988 return AAStrokeRectOp::Make(context, std::move(paint), viewMatrix, devOutside,
989 devInside, SkVector{dx, dy} * .5f);
990}
991
992} // namespace skgpu::ganesh::StrokeRectOp
993
994#if defined(GR_TEST_UTILS)
995
997
998GR_DRAW_OP_TEST_DEFINE(NonAAStrokeRectOp) {
999 SkMatrix viewMatrix = GrTest::TestMatrix(random);
1000 SkRect rect = GrTest::TestRect(random);
1001 SkScalar strokeWidth = random->nextBool() ? 0.0f : 2.0f;
1002 SkPaint strokePaint;
1003 strokePaint.setStrokeWidth(strokeWidth);
1004 strokePaint.setStyle(SkPaint::kStroke_Style);
1006 SkStrokeRec strokeRec(strokePaint);
1007 GrAAType aaType = GrAAType::kNone;
1008 if (numSamples > 1) {
1009 aaType = random->nextBool() ? GrAAType::kMSAA : GrAAType::kNone;
1010 }
1011 return skgpu::ganesh::StrokeRectOp::NonAAStrokeRectOp::Make(context, std::move(paint),
1012 viewMatrix, rect, strokeRec,
1013 aaType);
1014}
1015
1016GR_DRAW_OP_TEST_DEFINE(AAStrokeRectOp) {
1017 bool miterStroke = random->nextBool();
1018
1019 // Create either a empty rect or a non-empty rect.
1020 SkRect rect =
1021 random->nextBool() ? SkRect::MakeXYWH(10, 10, 50, 40) : SkRect::MakeXYWH(6, 7, 0, 0);
1022 SkScalar minDim = std::min(rect.width(), rect.height());
1023 SkScalar strokeWidth = random->nextUScalar1() * minDim;
1024
1026 rec.setStrokeStyle(strokeWidth);
1027 rec.setStrokeParams(SkPaint::kButt_Cap,
1028 miterStroke ? SkPaint::kMiter_Join : SkPaint::kBevel_Join, 1.f);
1029 SkMatrix matrix = GrTest::TestMatrixRectStaysRect(random);
1030 return skgpu::ganesh::StrokeRectOp::AAStrokeRectOp::Make(context, std::move(paint), matrix,
1031 rect, rec);
1032}
1033
1034#endif
SkMatrix fViewMatrix
static const int outset
Definition BlurTest.cpp:58
static const int strokeWidth
Definition BlurTest.cpp:60
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213
SkRect fRect
#define DEFINE_OP_CLASS_ID
Definition GrOp.h:64
GrClampType
std::function< void(GrSurfaceProxy *, skgpu::Mipmapped)> GrVisitProxyFunc
GrPrimitiveType
Definition GrTypesPriv.h:42
GrAAType
GrAA
GrLoadOp
GrXferBarrierFlags
SkColor4f color
#define SKGPU_DECLARE_STATIC_UNIQUE_KEY(name)
#define SKGPU_DEFINE_STATIC_UNIQUE_KEY(name)
#define SkASSERT(cond)
Definition SkAssert.h:116
constexpr SkPMColor4f SK_PMColor4fTRANSPARENT
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
static SkColorType colorType(AImageDecoder *decoder, const AImageDecoderHeaderInfo *headerInfo)
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition SkPath.cpp:3824
#define INHERITED(method,...)
#define SkScalarFloorToScalar(x)
Definition SkScalar.h:30
static bool SkScalarNearlyEqual(SkScalar x, SkScalar y, SkScalar tolerance=SK_ScalarNearlyZero)
Definition SkScalar.h:107
#define SK_Scalar1
Definition SkScalar.h:18
#define SkScalarHalf(a)
Definition SkScalar.h:75
#define SK_ScalarSqrt2
Definition SkScalar.h:20
SK_API SkString static SkString SkStringPrintf()
Definition SkString.h:287
SkRect fDevOutside
SkRect fDevOutsideAssist
SkVector fDevHalfStrokeSize
SkRect fDevInside
bool fDegenerate
const GrCaps * caps() const
bool avoidLineDraws() const
Definition GrCaps.h:555
bool reducedShaderMode() const
Definition GrCaps.h:190
int maxRenderTargetSize() const
Definition GrCaps.h:223
void drawMesh(const GrSimpleMesh &mesh)
void bindPipelineAndScissorClip(const GrProgramInfo &programInfo, const SkRect &drawBounds)
void bindTextures(const GrGeometryProcessor &geomProc, const GrSurfaceProxy &singleGeomProcTexture, const GrPipeline &pipeline)
Definition GrOp.h:70
CombineResult
Definition GrOp.h:99
std::unique_ptr< GrOp > Owner
Definition GrOp.h:72
const T & cast() const
Definition GrOp.h:148
const GrPipeline & pipeline() const
const GrGeometryProcessor & geomProc() const
void visitFPProxies(const GrVisitProxyFunc &func) const
static GrQuad MakeFromRect(const SkRect &, const SkMatrix &)
Definition GrQuad.cpp:107
GrRecordingContextPriv priv()
sk_sp< const GrGpuBuffer > findOrCreatePatternedIndexBuffer(const uint16_t *pattern, int patternSize, int reps, int vertCount, const skgpu::UniqueKey &key)
void visitProxies(const GrVisitProxyFunc &func) const
GrDrawOp::FixedFunctionFlags fixedFunctionFlags() const
GrProgramInfo * createProgramInfo(const GrCaps *, SkArenaAlloc *, const GrSurfaceProxyView &writeView, bool usesMSAASurface, GrAppliedClip &&, const GrDstProxyView &, GrGeometryProcessor *, GrPrimitiveType, GrXferBarrierFlags renderPassXferBarriers, GrLoadOp colorLoadOp)
GrProcessorSet::Analysis finalizeProcessors(const GrCaps &caps, const GrAppliedClip *clip, GrClampType clampType, GrProcessorAnalysisCoverage geometryCoverage, GrProcessorAnalysisColor *geometryColor)
static bool CheapEqual(const SkMatrix &a, const SkMatrix &b)
void mapVectors(SkVector dst[], const SkVector src[], int count) const
bool rectStaysRect() const
Definition SkMatrix.h:271
bool mapRect(SkRect *dst, const SkRect &src, SkApplyPerspectiveClip pc=SkApplyPerspectiveClip::kYes) const
@ kButt_Cap
no stroke extension
Definition SkPaint.h:334
void setStyle(Style style)
Definition SkPaint.cpp:105
@ kStroke_Style
set to stroke geometry
Definition SkPaint.h:194
void setStrokeJoin(Join join)
Definition SkPaint.cpp:189
@ kMiter_Join
extends to miter limit
Definition SkPaint.h:359
@ kBevel_Join
connects outside edges
Definition SkPaint.h:361
void setStrokeWidth(SkScalar width)
Definition SkPaint.cpp:159
void void void appendf(const char format[],...) SK_PRINTF_LIKE(2
Definition SkString.cpp:550
Style getStyle() const
bool isHairlineStyle() const
Definition SkStrokeRec.h:47
SkScalar getWidth() const
Definition SkStrokeRec.h:42
SkPaint::Join getJoin() const
Definition SkStrokeRec.h:45
SkScalar getMiter() const
Definition SkStrokeRec.h:43
static GrOp::Owner Make(GrRecordingContext *, GrPaint &&, GrAAType, DrawQuad *, const GrUserStencilSettings *=nullptr, InputFlags=InputFlags::kNone)
const Paint & paint
float SkScalar
Definition extension.cpp:12
uint32_t * target
const char * name
Definition fuchsia.cc:50
GrGeometryProcessor * Make(SkArenaAlloc *, const Color &, const Coverage &, const LocalCoords &, const SkMatrix &viewMatrix)
unsigned useCenter Optional< SkMatrix > matrix
Definition SkRecords.h:258
Optional< SkRect > bounds
Definition SkRecords.h:189
sk_sp< SkBlender > blender SkRect rect
Definition SkRecords.h:350
skia_private::AutoTArray< sk_sp< SkImageFilter > > filters TypedMatrix matrix TypedMatrix matrix SkScalar dx
Definition SkRecords.h:208
void Helper(uword arg)
sk_sp< const GrBuffer > GetIndexBuffer(GrMeshDrawTarget *target, IndexBufferOption indexBufferOption)
GrOp::Owner Make(GrRecordingContext *context, GrPaint &&paint, GrAAType aaType, const SkMatrix &viewMatrix, const SkRect &rect, const SkStrokeRec &stroke)
GrOp::Owner MakeNested(GrRecordingContext *context, GrPaint &&paint, const SkMatrix &viewMatrix, const SkRect rects[2])
static SkRect inset(const SkRect &r)
SkScalar w
SkScalar h
int32_t width
void set(sk_sp< const GrBuffer > vertexBuffer, int vertexCount, int baseVertex)
float fX
x-axis value
void setAbs(const SkPoint &pt)
void set(float x, float y)
float fY
y-axis value
SkScalar fBottom
larger y-axis bounds
Definition extension.cpp:17
void inset(float dx, float dy)
Definition SkRect.h:1060
bool intersect(const SkRect &r)
Definition SkRect.cpp:114
SkScalar fLeft
smaller x-axis bounds
Definition extension.cpp:14
void outset(float dx, float dy)
Definition SkRect.h:1077
SkRect makeOutset(float dx, float dy) const
Definition SkRect.h:1002
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition SkRect.h:659
SkRect makeInset(float dx, float dy) const
Definition SkRect.h:987
SkScalar fRight
larger x-axis bounds
Definition extension.cpp:16
constexpr float centerX() const
Definition SkRect.h:776
constexpr float height() const
Definition SkRect.h:769
constexpr float centerY() const
Definition SkRect.h:785
constexpr float width() const
Definition SkRect.h:762
bool isEmpty() const
Definition SkRect.h:693
void sort()
Definition SkRect.h:1313
static constexpr SkRect MakeWH(float w, float h)
Definition SkRect.h:609
SkScalar fTop
smaller y-axis bounds
Definition extension.cpp:15
static TriFan< float > TriFanFromRect(const SkRect &r)
static Conditional< T > If(bool condition, const T &value)