Flutter Engine
The Flutter Engine
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:
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);
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),
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:
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
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) {
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 }
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);
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
Definition: FillRRectOp.cpp:73
#define DEFINE_OP_CLASS_ID
Definition: GrOp.h:64
GrClampType
Definition: GrTypesPriv.h:228
std::function< void(GrSurfaceProxy *, skgpu::Mipmapped)> GrVisitProxyFunc
Definition: GrTypesPriv.h:943
GrPrimitiveType
Definition: GrTypesPriv.h:43
GrAAType
Definition: GrTypesPriv.h:200
GrAA
Definition: GrTypesPriv.h:173
GrLoadOp
Definition: GrTypesPriv.h:155
GrXferBarrierFlags
#define SKGPU_DEFINE_STATIC_UNIQUE_KEY(name)
Definition: ResourceKey.h:324
#define SkASSERT(cond)
Definition: SkAssert.h:116
constexpr SkPMColor4f SK_PMColor4fTRANSPARENT
Definition: SkColorData.h:378
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:3892
#define INHERITED(method,...)
Definition: SkRecorder.cpp:128
#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 SkStringPrintf(const char *format,...) SK_PRINTF_LIKE(1
Creates a new string and writes into it using a printf()-style format.
SkRect fDevOutside
SkRect fDevOutsideAssist
SkVector fDevHalfStrokeSize
SkRect fDevInside
bool fDegenerate
const GrCaps * caps() const
Definition: GrCaps.h:57
bool avoidLineDraws() const
Definition: GrCaps.h:555
bool reducedShaderMode() const
Definition: GrCaps.h:190
int maxRenderTargetSize() const
Definition: GrCaps.h:223
size_t vertexStride() const
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
Definition: GrProgramInfo.h:39
const GrGeometryProcessor & geomProc() const
Definition: GrProgramInfo.h:40
void visitFPProxies(const GrVisitProxyFunc &func) const
Definition: GrProgramInfo.h:64
Definition: GrQuad.h:30
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)
Definition: SkMatrixPriv.h:181
void mapVectors(SkVector dst[], const SkVector src[], int count) const
Definition: SkMatrix.cpp:1097
bool rectStaysRect() const
Definition: SkMatrix.h:271
bool mapRect(SkRect *dst, const SkRect &src, SkApplyPerspectiveClip pc=SkApplyPerspectiveClip::kYes) const
Definition: SkMatrix.cpp:1141
@ 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
static GrOp::Owner Make(GrRecordingContext *, GrPaint &&, GrAAType, DrawQuad *, const GrUserStencilSettings *=nullptr, InputFlags=InputFlags::kNone)
Definition: FillRectOp.cpp:470
const Paint & paint
Definition: color_source.cc:38
DlColor color
float SkScalar
Definition: extension.cpp:12
uint32_t * target
static float max(float r, float g, float b)
Definition: hsl.cpp:49
static float min(float r, float g, float b)
Definition: hsl.cpp:48
GrGeometryProcessor * Make(SkArenaAlloc *, const Color &, const Coverage &, const LocalCoords &, const SkMatrix &viewMatrix)
GrGeometryProcessor * MakeForDeviceSpace(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)
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
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])
SKGPU_DECLARE_STATIC_UNIQUE_KEY(gUnitQuadBufferKey)
static SkRect inset(const SkRect &r)
SkScalar w
SkScalar h
int32_t width
void set(sk_sp< const GrBuffer > vertexBuffer, int vertexCount, int baseVertex)
Definition: GrSimpleMesh.h:47
float fX
x-axis value
Definition: SkPoint_impl.h:164
void setAbs(const SkPoint &pt)
Definition: SkPoint_impl.h:236
void set(float x, float y)
Definition: SkPoint_impl.h:200
float fY
y-axis value
Definition: SkPoint_impl.h:165
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)
Definition: BufferWriter.h:239
static Conditional< T > If(bool condition, const T &value)
Definition: BufferWriter.h:153