GrStrokeRectOp.cpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827
  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. #include "src/gpu/ops/GrStrokeRectOp.h"
  8. #include "include/core/SkStrokeRec.h"
  9. #include "include/private/GrResourceKey.h"
  10. #include "include/utils/SkRandom.h"
  11. #include "src/gpu/GrCaps.h"
  12. #include "src/gpu/GrColor.h"
  13. #include "src/gpu/GrDefaultGeoProcFactory.h"
  14. #include "src/gpu/GrDrawOpTest.h"
  15. #include "src/gpu/GrOpFlushState.h"
  16. #include "src/gpu/GrResourceProvider.h"
  17. #include "src/gpu/GrVertexWriter.h"
  18. #include "src/gpu/ops/GrFillRectOp.h"
  19. #include "src/gpu/ops/GrMeshDrawOp.h"
  20. #include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
  21. namespace {
  22. // We support all hairlines, bevels, and miters, but not round joins. Also, check whether the miter
  23. // limit makes a miter join effectively beveled. If the miter is effectively beveled, it is only
  24. // supported when using an AA stroke.
  25. inline static bool allowed_stroke(const SkStrokeRec& stroke, GrAA aa, bool* isMiter) {
  26. SkASSERT(stroke.getStyle() == SkStrokeRec::kStroke_Style ||
  27. stroke.getStyle() == SkStrokeRec::kHairline_Style);
  28. // For hairlines, make bevel and round joins appear the same as mitered ones.
  29. if (!stroke.getWidth()) {
  30. *isMiter = true;
  31. return true;
  32. }
  33. if (stroke.getJoin() == SkPaint::kBevel_Join) {
  34. *isMiter = false;
  35. return aa == GrAA::kYes; // bevel only supported with AA
  36. }
  37. if (stroke.getJoin() == SkPaint::kMiter_Join) {
  38. *isMiter = stroke.getMiter() >= SK_ScalarSqrt2;
  39. // Supported under non-AA only if it remains mitered
  40. return aa == GrAA::kYes || *isMiter;
  41. }
  42. return false;
  43. }
  44. ///////////////////////////////////////////////////////////////////////////////////////////////////
  45. // Non-AA Stroking
  46. ///////////////////////////////////////////////////////////////////////////////////////////////////
  47. /* create a triangle strip that strokes the specified rect. There are 8
  48. unique vertices, but we repeat the last 2 to close up. Alternatively we
  49. could use an indices array, and then only send 8 verts, but not sure that
  50. would be faster.
  51. */
  52. static void init_nonaa_stroke_rect_strip(SkPoint verts[10], const SkRect& rect, SkScalar width) {
  53. const SkScalar rad = SkScalarHalf(width);
  54. verts[0].set(rect.fLeft + rad, rect.fTop + rad);
  55. verts[1].set(rect.fLeft - rad, rect.fTop - rad);
  56. verts[2].set(rect.fRight - rad, rect.fTop + rad);
  57. verts[3].set(rect.fRight + rad, rect.fTop - rad);
  58. verts[4].set(rect.fRight - rad, rect.fBottom - rad);
  59. verts[5].set(rect.fRight + rad, rect.fBottom + rad);
  60. verts[6].set(rect.fLeft + rad, rect.fBottom - rad);
  61. verts[7].set(rect.fLeft - rad, rect.fBottom + rad);
  62. verts[8] = verts[0];
  63. verts[9] = verts[1];
  64. // TODO: we should be catching this higher up the call stack and just draw a single
  65. // non-AA rect
  66. if (2*rad >= rect.width()) {
  67. verts[0].fX = verts[2].fX = verts[4].fX = verts[6].fX = verts[8].fX = rect.centerX();
  68. }
  69. if (2*rad >= rect.height()) {
  70. verts[0].fY = verts[2].fY = verts[4].fY = verts[6].fY = verts[8].fY = rect.centerY();
  71. }
  72. }
  73. class NonAAStrokeRectOp final : public GrMeshDrawOp {
  74. private:
  75. using Helper = GrSimpleMeshDrawOpHelper;
  76. public:
  77. DEFINE_OP_CLASS_ID
  78. const char* name() const override { return "NonAAStrokeRectOp"; }
  79. void visitProxies(const VisitProxyFunc& func) const override {
  80. fHelper.visitProxies(func);
  81. }
  82. #ifdef SK_DEBUG
  83. SkString dumpInfo() const override {
  84. SkString string;
  85. string.appendf(
  86. "Color: 0x%08x, Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
  87. "StrokeWidth: %.2f\n",
  88. fColor.toBytes_RGBA(), fRect.fLeft, fRect.fTop, fRect.fRight, fRect.fBottom,
  89. fStrokeWidth);
  90. string += fHelper.dumpInfo();
  91. string += INHERITED::dumpInfo();
  92. return string;
  93. }
  94. #endif
  95. static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
  96. GrPaint&& paint,
  97. const SkMatrix& viewMatrix,
  98. const SkRect& rect,
  99. const SkStrokeRec& stroke,
  100. GrAAType aaType) {
  101. bool isMiter;
  102. if (!allowed_stroke(stroke, GrAA::kNo, &isMiter)) {
  103. return nullptr;
  104. }
  105. Helper::InputFlags inputFlags = Helper::InputFlags::kNone;
  106. // Depending on sub-pixel coordinates and the particular GPU, we may lose a corner of
  107. // hairline rects. We jam all the vertices to pixel centers to avoid this, but not
  108. // when MSAA is enabled because it can cause ugly artifacts.
  109. if (stroke.getStyle() == SkStrokeRec::kHairline_Style && aaType != GrAAType::kMSAA) {
  110. inputFlags |= Helper::InputFlags::kSnapVerticesToPixelCenters;
  111. }
  112. return Helper::FactoryHelper<NonAAStrokeRectOp>(context, std::move(paint), inputFlags,
  113. viewMatrix, rect,
  114. stroke, aaType);
  115. }
  116. NonAAStrokeRectOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
  117. Helper::InputFlags inputFlags, const SkMatrix& viewMatrix, const SkRect& rect,
  118. const SkStrokeRec& stroke, GrAAType aaType)
  119. : INHERITED(ClassID()), fHelper(helperArgs, aaType, inputFlags) {
  120. fColor = color;
  121. fViewMatrix = viewMatrix;
  122. fRect = rect;
  123. // Sort the rect for hairlines
  124. fRect.sort();
  125. fStrokeWidth = stroke.getWidth();
  126. SkScalar rad = SkScalarHalf(fStrokeWidth);
  127. SkRect bounds = rect;
  128. bounds.outset(rad, rad);
  129. // If our caller snaps to pixel centers then we have to round out the bounds
  130. if (inputFlags & Helper::InputFlags::kSnapVerticesToPixelCenters) {
  131. viewMatrix.mapRect(&bounds);
  132. // We want to be consistent with how we snap non-aa lines. To match what we do in
  133. // GrGLSLVertexShaderBuilder, we first floor all the vertex values and then add half a
  134. // pixel to force us to pixel centers.
  135. bounds.set(SkScalarFloorToScalar(bounds.fLeft),
  136. SkScalarFloorToScalar(bounds.fTop),
  137. SkScalarFloorToScalar(bounds.fRight),
  138. SkScalarFloorToScalar(bounds.fBottom));
  139. bounds.offset(0.5f, 0.5f);
  140. this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
  141. } else {
  142. this->setTransformedBounds(bounds, fViewMatrix, HasAABloat::kNo, IsZeroArea::kNo);
  143. }
  144. }
  145. FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
  146. GrProcessorSet::Analysis finalize(
  147. const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
  148. GrClampType clampType) override {
  149. // This Op uses uniform (not vertex) color, so doesn't need to track wide color.
  150. return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
  151. GrProcessorAnalysisCoverage::kNone, &fColor, nullptr);
  152. }
  153. private:
  154. void onPrepareDraws(Target* target) override {
  155. sk_sp<GrGeometryProcessor> gp;
  156. {
  157. using namespace GrDefaultGeoProcFactory;
  158. Color color(fColor);
  159. LocalCoords::Type localCoordsType = fHelper.usesLocalCoords()
  160. ? LocalCoords::kUsePosition_Type
  161. : LocalCoords::kUnused_Type;
  162. gp = GrDefaultGeoProcFactory::Make(target->caps().shaderCaps(), color,
  163. Coverage::kSolid_Type, localCoordsType,
  164. fViewMatrix);
  165. }
  166. size_t kVertexStride = gp->vertexStride();
  167. int vertexCount = kVertsPerHairlineRect;
  168. if (fStrokeWidth > 0) {
  169. vertexCount = kVertsPerStrokeRect;
  170. }
  171. sk_sp<const GrBuffer> vertexBuffer;
  172. int firstVertex;
  173. void* verts =
  174. target->makeVertexSpace(kVertexStride, vertexCount, &vertexBuffer, &firstVertex);
  175. if (!verts) {
  176. SkDebugf("Could not allocate vertices\n");
  177. return;
  178. }
  179. SkPoint* vertex = reinterpret_cast<SkPoint*>(verts);
  180. GrPrimitiveType primType;
  181. if (fStrokeWidth > 0) {
  182. primType = GrPrimitiveType::kTriangleStrip;
  183. init_nonaa_stroke_rect_strip(vertex, fRect, fStrokeWidth);
  184. } else {
  185. // hairline
  186. primType = GrPrimitiveType::kLineStrip;
  187. vertex[0].set(fRect.fLeft, fRect.fTop);
  188. vertex[1].set(fRect.fRight, fRect.fTop);
  189. vertex[2].set(fRect.fRight, fRect.fBottom);
  190. vertex[3].set(fRect.fLeft, fRect.fBottom);
  191. vertex[4].set(fRect.fLeft, fRect.fTop);
  192. }
  193. GrMesh* mesh = target->allocMesh(primType);
  194. mesh->setNonIndexedNonInstanced(vertexCount);
  195. mesh->setVertexData(std::move(vertexBuffer), firstVertex);
  196. target->recordDraw(std::move(gp), mesh);
  197. }
  198. void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
  199. fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
  200. }
  201. // TODO: override onCombineIfPossible
  202. Helper fHelper;
  203. SkPMColor4f fColor;
  204. SkMatrix fViewMatrix;
  205. SkRect fRect;
  206. SkScalar fStrokeWidth;
  207. const static int kVertsPerHairlineRect = 5;
  208. const static int kVertsPerStrokeRect = 10;
  209. typedef GrMeshDrawOp INHERITED;
  210. };
  211. ///////////////////////////////////////////////////////////////////////////////////////////////////
  212. // AA Stroking
  213. ///////////////////////////////////////////////////////////////////////////////////////////////////
  214. GR_DECLARE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
  215. GR_DECLARE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
  216. static void compute_aa_rects(SkRect* devOutside, SkRect* devOutsideAssist, SkRect* devInside,
  217. bool* isDegenerate, const SkMatrix& viewMatrix, const SkRect& rect,
  218. SkScalar strokeWidth, bool miterStroke) {
  219. SkRect devRect;
  220. viewMatrix.mapRect(&devRect, rect);
  221. SkVector devStrokeSize;
  222. if (strokeWidth > 0) {
  223. devStrokeSize.set(strokeWidth, strokeWidth);
  224. viewMatrix.mapVectors(&devStrokeSize, 1);
  225. devStrokeSize.setAbs(devStrokeSize);
  226. } else {
  227. devStrokeSize.set(SK_Scalar1, SK_Scalar1);
  228. }
  229. const SkScalar dx = devStrokeSize.fX;
  230. const SkScalar dy = devStrokeSize.fY;
  231. const SkScalar rx = SkScalarHalf(dx);
  232. const SkScalar ry = SkScalarHalf(dy);
  233. *devOutside = devRect;
  234. *devOutsideAssist = devRect;
  235. *devInside = devRect;
  236. devOutside->outset(rx, ry);
  237. devInside->inset(rx, ry);
  238. // If we have a degenerate stroking rect(ie the stroke is larger than inner rect) then we
  239. // make a degenerate inside rect to avoid double hitting. We will also jam all of the points
  240. // together when we render these rects.
  241. SkScalar spare;
  242. {
  243. SkScalar w = devRect.width() - dx;
  244. SkScalar h = devRect.height() - dy;
  245. spare = SkTMin(w, h);
  246. }
  247. *isDegenerate = spare <= 0;
  248. if (*isDegenerate) {
  249. devInside->fLeft = devInside->fRight = devRect.centerX();
  250. devInside->fTop = devInside->fBottom = devRect.centerY();
  251. }
  252. // For bevel-stroke, use 2 SkRect instances(devOutside and devOutsideAssist)
  253. // to draw the outside of the octagon. Because there are 8 vertices on the outer
  254. // edge, while vertex number of inner edge is 4, the same as miter-stroke.
  255. if (!miterStroke) {
  256. devOutside->inset(0, ry);
  257. devOutsideAssist->outset(0, ry);
  258. }
  259. }
  260. static sk_sp<GrGeometryProcessor> create_aa_stroke_rect_gp(const GrShaderCaps* shaderCaps,
  261. bool tweakAlphaForCoverage,
  262. const SkMatrix& viewMatrix,
  263. bool usesLocalCoords,
  264. bool wideColor) {
  265. using namespace GrDefaultGeoProcFactory;
  266. Coverage::Type coverageType =
  267. tweakAlphaForCoverage ? Coverage::kSolid_Type : Coverage::kAttribute_Type;
  268. LocalCoords::Type localCoordsType =
  269. usesLocalCoords ? LocalCoords::kUsePosition_Type : LocalCoords::kUnused_Type;
  270. Color::Type colorType =
  271. wideColor ? Color::kPremulWideColorAttribute_Type: Color::kPremulGrColorAttribute_Type;
  272. return MakeForDeviceSpace(shaderCaps, colorType, coverageType, localCoordsType, viewMatrix);
  273. }
  274. class AAStrokeRectOp final : public GrMeshDrawOp {
  275. private:
  276. using Helper = GrSimpleMeshDrawOpHelper;
  277. public:
  278. DEFINE_OP_CLASS_ID
  279. static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
  280. GrPaint&& paint,
  281. const SkMatrix& viewMatrix,
  282. const SkRect& devOutside,
  283. const SkRect& devInside) {
  284. return Helper::FactoryHelper<AAStrokeRectOp>(context, std::move(paint), viewMatrix,
  285. devOutside, devInside);
  286. }
  287. AAStrokeRectOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
  288. const SkMatrix& viewMatrix, const SkRect& devOutside, const SkRect& devInside)
  289. : INHERITED(ClassID())
  290. , fHelper(helperArgs, GrAAType::kCoverage)
  291. , fViewMatrix(viewMatrix) {
  292. SkASSERT(!devOutside.isEmpty());
  293. SkASSERT(!devInside.isEmpty());
  294. fRects.emplace_back(RectInfo{color, devOutside, devOutside, devInside, false});
  295. this->setBounds(devOutside, HasAABloat::kYes, IsZeroArea::kNo);
  296. fMiterStroke = true;
  297. }
  298. static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
  299. GrPaint&& paint,
  300. const SkMatrix& viewMatrix,
  301. const SkRect& rect,
  302. const SkStrokeRec& stroke) {
  303. bool isMiter;
  304. if (!allowed_stroke(stroke, GrAA::kYes, &isMiter)) {
  305. return nullptr;
  306. }
  307. return Helper::FactoryHelper<AAStrokeRectOp>(context, std::move(paint), viewMatrix, rect,
  308. stroke, isMiter);
  309. }
  310. AAStrokeRectOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
  311. const SkMatrix& viewMatrix, const SkRect& rect, const SkStrokeRec& stroke,
  312. bool isMiter)
  313. : INHERITED(ClassID())
  314. , fHelper(helperArgs, GrAAType::kCoverage)
  315. , fViewMatrix(viewMatrix) {
  316. fMiterStroke = isMiter;
  317. RectInfo& info = fRects.push_back();
  318. compute_aa_rects(&info.fDevOutside, &info.fDevOutsideAssist, &info.fDevInside,
  319. &info.fDegenerate, viewMatrix, rect, stroke.getWidth(), isMiter);
  320. info.fColor = color;
  321. if (isMiter) {
  322. this->setBounds(info.fDevOutside, HasAABloat::kYes, IsZeroArea::kNo);
  323. } else {
  324. // The outer polygon of the bevel stroke is an octagon specified by the points of a
  325. // pair of overlapping rectangles where one is wide and the other is narrow.
  326. SkRect bounds = info.fDevOutside;
  327. bounds.joinPossiblyEmptyRect(info.fDevOutsideAssist);
  328. this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
  329. }
  330. }
  331. const char* name() const override { return "AAStrokeRect"; }
  332. void visitProxies(const VisitProxyFunc& func) const override {
  333. fHelper.visitProxies(func);
  334. }
  335. #ifdef SK_DEBUG
  336. SkString dumpInfo() const override {
  337. SkString string;
  338. for (const auto& info : fRects) {
  339. string.appendf(
  340. "Color: 0x%08x, ORect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
  341. "AssistORect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
  342. "IRect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], Degen: %d",
  343. info.fColor.toBytes_RGBA(), info.fDevOutside.fLeft, info.fDevOutside.fTop,
  344. info.fDevOutside.fRight, info.fDevOutside.fBottom, info.fDevOutsideAssist.fLeft,
  345. info.fDevOutsideAssist.fTop, info.fDevOutsideAssist.fRight,
  346. info.fDevOutsideAssist.fBottom, info.fDevInside.fLeft, info.fDevInside.fTop,
  347. info.fDevInside.fRight, info.fDevInside.fBottom, info.fDegenerate);
  348. }
  349. string += fHelper.dumpInfo();
  350. string += INHERITED::dumpInfo();
  351. return string;
  352. }
  353. #endif
  354. FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
  355. GrProcessorSet::Analysis finalize(
  356. const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
  357. GrClampType clampType) override {
  358. return fHelper.finalizeProcessors(
  359. caps, clip, hasMixedSampledCoverage, clampType,
  360. GrProcessorAnalysisCoverage::kSingleChannel, &fRects.back().fColor, &fWideColor);
  361. }
  362. private:
  363. void onPrepareDraws(Target*) override;
  364. void onExecute(GrOpFlushState*, const SkRect& chainBounds) override;
  365. static const int kMiterIndexCnt = 3 * 24;
  366. static const int kMiterVertexCnt = 16;
  367. static const int kNumMiterRectsInIndexBuffer = 256;
  368. static const int kBevelIndexCnt = 48 + 36 + 24;
  369. static const int kBevelVertexCnt = 24;
  370. static const int kNumBevelRectsInIndexBuffer = 256;
  371. static sk_sp<const GrGpuBuffer> GetIndexBuffer(GrResourceProvider*, bool miterStroke);
  372. const SkMatrix& viewMatrix() const { return fViewMatrix; }
  373. bool miterStroke() const { return fMiterStroke; }
  374. CombineResult onCombineIfPossible(GrOp* t, const GrCaps&) override;
  375. void generateAAStrokeRectGeometry(GrVertexWriter& vertices,
  376. const SkPMColor4f& color,
  377. bool wideColor,
  378. const SkRect& devOutside,
  379. const SkRect& devOutsideAssist,
  380. const SkRect& devInside,
  381. bool miterStroke,
  382. bool degenerate,
  383. bool tweakAlphaForCoverage) const;
  384. // TODO support AA rotated stroke rects by copying around view matrices
  385. struct RectInfo {
  386. SkPMColor4f fColor;
  387. SkRect fDevOutside;
  388. SkRect fDevOutsideAssist;
  389. SkRect fDevInside;
  390. bool fDegenerate;
  391. };
  392. Helper fHelper;
  393. SkSTArray<1, RectInfo, true> fRects;
  394. SkMatrix fViewMatrix;
  395. bool fMiterStroke;
  396. bool fWideColor;
  397. typedef GrMeshDrawOp INHERITED;
  398. };
  399. void AAStrokeRectOp::onPrepareDraws(Target* target) {
  400. sk_sp<GrGeometryProcessor> gp(create_aa_stroke_rect_gp(target->caps().shaderCaps(),
  401. fHelper.compatibleWithCoverageAsAlpha(),
  402. this->viewMatrix(),
  403. fHelper.usesLocalCoords(),
  404. fWideColor));
  405. if (!gp) {
  406. SkDebugf("Couldn't create GrGeometryProcessor\n");
  407. return;
  408. }
  409. int innerVertexNum = 4;
  410. int outerVertexNum = this->miterStroke() ? 4 : 8;
  411. int verticesPerInstance = (outerVertexNum + innerVertexNum) * 2;
  412. int indicesPerInstance = this->miterStroke() ? kMiterIndexCnt : kBevelIndexCnt;
  413. int instanceCount = fRects.count();
  414. sk_sp<const GrGpuBuffer> indexBuffer =
  415. GetIndexBuffer(target->resourceProvider(), this->miterStroke());
  416. if (!indexBuffer) {
  417. SkDebugf("Could not allocate indices\n");
  418. return;
  419. }
  420. PatternHelper helper(target, GrPrimitiveType::kTriangles, gp->vertexStride(),
  421. std::move(indexBuffer), verticesPerInstance, indicesPerInstance,
  422. instanceCount);
  423. GrVertexWriter vertices{ helper.vertices() };
  424. if (!vertices.fPtr) {
  425. SkDebugf("Could not allocate vertices\n");
  426. return;
  427. }
  428. for (int i = 0; i < instanceCount; i++) {
  429. const RectInfo& info = fRects[i];
  430. this->generateAAStrokeRectGeometry(vertices,
  431. info.fColor,
  432. fWideColor,
  433. info.fDevOutside,
  434. info.fDevOutsideAssist,
  435. info.fDevInside,
  436. fMiterStroke,
  437. info.fDegenerate,
  438. fHelper.compatibleWithCoverageAsAlpha());
  439. }
  440. helper.recordDraw(target, std::move(gp));
  441. }
  442. void AAStrokeRectOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
  443. fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
  444. }
  445. sk_sp<const GrGpuBuffer> AAStrokeRectOp::GetIndexBuffer(GrResourceProvider* resourceProvider,
  446. bool miterStroke) {
  447. if (miterStroke) {
  448. // clang-format off
  449. static const uint16_t gMiterIndices[] = {
  450. 0 + 0, 1 + 0, 5 + 0, 5 + 0, 4 + 0, 0 + 0,
  451. 1 + 0, 2 + 0, 6 + 0, 6 + 0, 5 + 0, 1 + 0,
  452. 2 + 0, 3 + 0, 7 + 0, 7 + 0, 6 + 0, 2 + 0,
  453. 3 + 0, 0 + 0, 4 + 0, 4 + 0, 7 + 0, 3 + 0,
  454. 0 + 4, 1 + 4, 5 + 4, 5 + 4, 4 + 4, 0 + 4,
  455. 1 + 4, 2 + 4, 6 + 4, 6 + 4, 5 + 4, 1 + 4,
  456. 2 + 4, 3 + 4, 7 + 4, 7 + 4, 6 + 4, 2 + 4,
  457. 3 + 4, 0 + 4, 4 + 4, 4 + 4, 7 + 4, 3 + 4,
  458. 0 + 8, 1 + 8, 5 + 8, 5 + 8, 4 + 8, 0 + 8,
  459. 1 + 8, 2 + 8, 6 + 8, 6 + 8, 5 + 8, 1 + 8,
  460. 2 + 8, 3 + 8, 7 + 8, 7 + 8, 6 + 8, 2 + 8,
  461. 3 + 8, 0 + 8, 4 + 8, 4 + 8, 7 + 8, 3 + 8,
  462. };
  463. // clang-format on
  464. GR_STATIC_ASSERT(SK_ARRAY_COUNT(gMiterIndices) == kMiterIndexCnt);
  465. GR_DEFINE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
  466. return resourceProvider->findOrCreatePatternedIndexBuffer(
  467. gMiterIndices, kMiterIndexCnt, kNumMiterRectsInIndexBuffer, kMiterVertexCnt,
  468. gMiterIndexBufferKey);
  469. } else {
  470. /**
  471. * As in miter-stroke, index = a + b, and a is the current index, b is the shift
  472. * from the first index. The index layout:
  473. * outer AA line: 0~3, 4~7
  474. * outer edge: 8~11, 12~15
  475. * inner edge: 16~19
  476. * inner AA line: 20~23
  477. * Following comes a bevel-stroke rect and its indices:
  478. *
  479. * 4 7
  480. * *********************************
  481. * * ______________________________ *
  482. * * / 12 15 \ *
  483. * * / \ *
  484. * 0 * |8 16_____________________19 11 | * 3
  485. * * | | | | *
  486. * * | | **************** | | *
  487. * * | | * 20 23 * | | *
  488. * * | | * * | | *
  489. * * | | * 21 22 * | | *
  490. * * | | **************** | | *
  491. * * | |____________________| | *
  492. * 1 * |9 17 18 10| * 2
  493. * * \ / *
  494. * * \13 __________________________14/ *
  495. * * *
  496. * **********************************
  497. * 5 6
  498. */
  499. // clang-format off
  500. static const uint16_t gBevelIndices[] = {
  501. // Draw outer AA, from outer AA line to outer edge, shift is 0.
  502. 0 + 0, 1 + 0, 9 + 0, 9 + 0, 8 + 0, 0 + 0,
  503. 1 + 0, 5 + 0, 13 + 0, 13 + 0, 9 + 0, 1 + 0,
  504. 5 + 0, 6 + 0, 14 + 0, 14 + 0, 13 + 0, 5 + 0,
  505. 6 + 0, 2 + 0, 10 + 0, 10 + 0, 14 + 0, 6 + 0,
  506. 2 + 0, 3 + 0, 11 + 0, 11 + 0, 10 + 0, 2 + 0,
  507. 3 + 0, 7 + 0, 15 + 0, 15 + 0, 11 + 0, 3 + 0,
  508. 7 + 0, 4 + 0, 12 + 0, 12 + 0, 15 + 0, 7 + 0,
  509. 4 + 0, 0 + 0, 8 + 0, 8 + 0, 12 + 0, 4 + 0,
  510. // Draw the stroke, from outer edge to inner edge, shift is 8.
  511. 0 + 8, 1 + 8, 9 + 8, 9 + 8, 8 + 8, 0 + 8,
  512. 1 + 8, 5 + 8, 9 + 8,
  513. 5 + 8, 6 + 8, 10 + 8, 10 + 8, 9 + 8, 5 + 8,
  514. 6 + 8, 2 + 8, 10 + 8,
  515. 2 + 8, 3 + 8, 11 + 8, 11 + 8, 10 + 8, 2 + 8,
  516. 3 + 8, 7 + 8, 11 + 8,
  517. 7 + 8, 4 + 8, 8 + 8, 8 + 8, 11 + 8, 7 + 8,
  518. 4 + 8, 0 + 8, 8 + 8,
  519. // Draw the inner AA, from inner edge to inner AA line, shift is 16.
  520. 0 + 16, 1 + 16, 5 + 16, 5 + 16, 4 + 16, 0 + 16,
  521. 1 + 16, 2 + 16, 6 + 16, 6 + 16, 5 + 16, 1 + 16,
  522. 2 + 16, 3 + 16, 7 + 16, 7 + 16, 6 + 16, 2 + 16,
  523. 3 + 16, 0 + 16, 4 + 16, 4 + 16, 7 + 16, 3 + 16,
  524. };
  525. // clang-format on
  526. GR_STATIC_ASSERT(SK_ARRAY_COUNT(gBevelIndices) == kBevelIndexCnt);
  527. GR_DEFINE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
  528. return resourceProvider->findOrCreatePatternedIndexBuffer(
  529. gBevelIndices, kBevelIndexCnt, kNumBevelRectsInIndexBuffer, kBevelVertexCnt,
  530. gBevelIndexBufferKey);
  531. }
  532. }
  533. GrOp::CombineResult AAStrokeRectOp::onCombineIfPossible(GrOp* t, const GrCaps& caps) {
  534. AAStrokeRectOp* that = t->cast<AAStrokeRectOp>();
  535. if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
  536. return CombineResult::kCannotCombine;
  537. }
  538. // TODO combine across miterstroke changes
  539. if (this->miterStroke() != that->miterStroke()) {
  540. return CombineResult::kCannotCombine;
  541. }
  542. // We apply the viewmatrix to the rect points on the cpu. However, if the pipeline uses
  543. // local coords then we won't be able to combine. TODO: Upload local coords as an attribute.
  544. if (fHelper.usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
  545. return CombineResult::kCannotCombine;
  546. }
  547. fRects.push_back_n(that->fRects.count(), that->fRects.begin());
  548. fWideColor |= that->fWideColor;
  549. return CombineResult::kMerged;
  550. }
  551. static void setup_scale(int* scale, SkScalar inset) {
  552. if (inset < SK_ScalarHalf) {
  553. *scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf));
  554. SkASSERT(*scale >= 0 && *scale <= 255);
  555. } else {
  556. *scale = 0xff;
  557. }
  558. }
  559. void AAStrokeRectOp::generateAAStrokeRectGeometry(GrVertexWriter& vertices,
  560. const SkPMColor4f& color,
  561. bool wideColor,
  562. const SkRect& devOutside,
  563. const SkRect& devOutsideAssist,
  564. const SkRect& devInside,
  565. bool miterStroke,
  566. bool degenerate,
  567. bool tweakAlphaForCoverage) const {
  568. // We create vertices for four nested rectangles. There are two ramps from 0 to full
  569. // coverage, one on the exterior of the stroke and the other on the interior.
  570. // TODO: this only really works if the X & Y margins are the same all around
  571. // the rect (or if they are all >= 1.0).
  572. SkScalar inset;
  573. if (!degenerate) {
  574. inset = SkMinScalar(SK_Scalar1, devOutside.fRight - devInside.fRight);
  575. inset = SkMinScalar(inset, devInside.fLeft - devOutside.fLeft);
  576. inset = SkMinScalar(inset, devInside.fTop - devOutside.fTop);
  577. if (miterStroke) {
  578. inset = SK_ScalarHalf * SkMinScalar(inset, devOutside.fBottom - devInside.fBottom);
  579. } else {
  580. inset = SK_ScalarHalf *
  581. SkMinScalar(inset, devOutsideAssist.fBottom - devInside.fBottom);
  582. }
  583. SkASSERT(inset >= 0);
  584. } else {
  585. // TODO use real devRect here
  586. inset = SkMinScalar(devOutside.width(), SK_Scalar1);
  587. inset = SK_ScalarHalf *
  588. SkMinScalar(inset, SkTMax(devOutside.height(), devOutsideAssist.height()));
  589. }
  590. auto inset_fan = [](const SkRect& r, SkScalar dx, SkScalar dy) {
  591. return GrVertexWriter::TriFanFromRect(r.makeInset(dx, dy));
  592. };
  593. auto maybe_coverage = [tweakAlphaForCoverage](float coverage) {
  594. return GrVertexWriter::If(!tweakAlphaForCoverage, coverage);
  595. };
  596. GrVertexColor outerColor(tweakAlphaForCoverage ? SK_PMColor4fTRANSPARENT : color, wideColor);
  597. // Outermost rect
  598. vertices.writeQuad(inset_fan(devOutside, -SK_ScalarHalf, -SK_ScalarHalf),
  599. outerColor,
  600. maybe_coverage(0.0f));
  601. if (!miterStroke) {
  602. // Second outermost
  603. vertices.writeQuad(inset_fan(devOutsideAssist, -SK_ScalarHalf, -SK_ScalarHalf),
  604. outerColor,
  605. maybe_coverage(0.0f));
  606. }
  607. // scale is the coverage for the the inner two rects.
  608. int scale;
  609. setup_scale(&scale, inset);
  610. float innerCoverage = GrNormalizeByteToFloat(scale);
  611. SkPMColor4f scaledColor = color * innerCoverage;
  612. GrVertexColor innerColor(tweakAlphaForCoverage ? scaledColor : color, wideColor);
  613. // Inner rect
  614. vertices.writeQuad(inset_fan(devOutside, inset, inset),
  615. innerColor,
  616. maybe_coverage(innerCoverage));
  617. if (!miterStroke) {
  618. // Second inner
  619. vertices.writeQuad(inset_fan(devOutsideAssist, inset, inset),
  620. innerColor,
  621. maybe_coverage(innerCoverage));
  622. }
  623. if (!degenerate) {
  624. vertices.writeQuad(inset_fan(devInside, -inset, -inset),
  625. innerColor,
  626. maybe_coverage(innerCoverage));
  627. // The innermost rect has 0 coverage...
  628. vertices.writeQuad(inset_fan(devInside, SK_ScalarHalf, SK_ScalarHalf),
  629. GrVertexColor(SK_PMColor4fTRANSPARENT, wideColor),
  630. maybe_coverage(0.0f));
  631. } else {
  632. // When the interior rect has become degenerate we smoosh to a single point
  633. SkASSERT(devInside.fLeft == devInside.fRight && devInside.fTop == devInside.fBottom);
  634. vertices.writeQuad(GrVertexWriter::TriFanFromRect(devInside),
  635. innerColor,
  636. maybe_coverage(innerCoverage));
  637. // ... unless we are degenerate, in which case we must apply the scaled coverage
  638. vertices.writeQuad(GrVertexWriter::TriFanFromRect(devInside),
  639. innerColor,
  640. maybe_coverage(innerCoverage));
  641. }
  642. }
  643. } // anonymous namespace
  644. namespace GrStrokeRectOp {
  645. std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
  646. GrPaint&& paint,
  647. GrAAType aaType,
  648. const SkMatrix& viewMatrix,
  649. const SkRect& rect,
  650. const SkStrokeRec& stroke) {
  651. if (aaType == GrAAType::kCoverage) {
  652. // The AA op only supports axis-aligned rectangles
  653. if (!viewMatrix.rectStaysRect()) {
  654. return nullptr;
  655. }
  656. return AAStrokeRectOp::Make(context, std::move(paint), viewMatrix, rect, stroke);
  657. } else {
  658. return NonAAStrokeRectOp::Make(context, std::move(paint), viewMatrix, rect, stroke, aaType);
  659. }
  660. }
  661. std::unique_ptr<GrDrawOp> MakeNested(GrRecordingContext* context,
  662. GrPaint&& paint,
  663. const SkMatrix& viewMatrix,
  664. const SkRect rects[2]) {
  665. SkASSERT(viewMatrix.rectStaysRect());
  666. SkASSERT(!rects[0].isEmpty() && !rects[1].isEmpty());
  667. SkRect devOutside, devInside;
  668. viewMatrix.mapRect(&devOutside, rects[0]);
  669. viewMatrix.mapRect(&devInside, rects[1]);
  670. if (devInside.isEmpty()) {
  671. if (devOutside.isEmpty()) {
  672. return nullptr;
  673. }
  674. return GrFillRectOp::Make(context, std::move(paint), GrAAType::kCoverage,
  675. GrQuadAAFlags::kAll,
  676. GrQuad::MakeFromRect(rects[0], viewMatrix),
  677. GrQuad(rects[0]));
  678. }
  679. return AAStrokeRectOp::Make(context, std::move(paint), viewMatrix, devOutside, devInside);
  680. }
  681. } // namespace GrStrokeRectOp
  682. #if GR_TEST_UTILS
  683. #include "src/gpu/GrDrawOpTest.h"
  684. GR_DRAW_OP_TEST_DEFINE(NonAAStrokeRectOp) {
  685. SkMatrix viewMatrix = GrTest::TestMatrix(random);
  686. SkRect rect = GrTest::TestRect(random);
  687. SkScalar strokeWidth = random->nextBool() ? 0.0f : 2.0f;
  688. SkPaint strokePaint;
  689. strokePaint.setStrokeWidth(strokeWidth);
  690. strokePaint.setStyle(SkPaint::kStroke_Style);
  691. strokePaint.setStrokeJoin(SkPaint::kMiter_Join);
  692. SkStrokeRec strokeRec(strokePaint);
  693. GrAAType aaType = GrAAType::kNone;
  694. if (numSamples > 1) {
  695. aaType = random->nextBool() ? GrAAType::kMSAA : GrAAType::kNone;
  696. }
  697. return NonAAStrokeRectOp::Make(context, std::move(paint), viewMatrix, rect, strokeRec, aaType);
  698. }
  699. GR_DRAW_OP_TEST_DEFINE(AAStrokeRectOp) {
  700. bool miterStroke = random->nextBool();
  701. // Create either a empty rect or a non-empty rect.
  702. SkRect rect =
  703. random->nextBool() ? SkRect::MakeXYWH(10, 10, 50, 40) : SkRect::MakeXYWH(6, 7, 0, 0);
  704. SkScalar minDim = SkMinScalar(rect.width(), rect.height());
  705. SkScalar strokeWidth = random->nextUScalar1() * minDim;
  706. SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
  707. rec.setStrokeStyle(strokeWidth);
  708. rec.setStrokeParams(SkPaint::kButt_Cap,
  709. miterStroke ? SkPaint::kMiter_Join : SkPaint::kBevel_Join, 1.f);
  710. SkMatrix matrix = GrTest::TestMatrixRectStaysRect(random);
  711. return AAStrokeRectOp::Make(context, std::move(paint), matrix, rect, rec);
  712. }
  713. #endif