1 /*
2 * Copyright 2015 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "GrDefaultGeoProcFactory.h"
9 #include "GrOpFlushState.h"
10 #include "GrRectOpFactory.h"
11 #include "GrResourceKey.h"
12 #include "GrResourceProvider.h"
13 #include "GrSimpleMeshDrawOpHelper.h"
14 #include "SkPointPriv.h"
15 #include "SkStrokeRec.h"
16
17 GR_DECLARE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
18 GR_DECLARE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
19
set_inset_fan(SkPoint * pts,size_t stride,const SkRect & r,SkScalar dx,SkScalar dy)20 static void set_inset_fan(SkPoint* pts, size_t stride, const SkRect& r, SkScalar dx, SkScalar dy) {
21 SkPointPriv::SetRectFan(pts, r.fLeft + dx, r.fTop + dy, r.fRight - dx, r.fBottom - dy, stride);
22 }
23
24 // We support all hairlines, bevels, and miters, but not round joins. Also, check whether the miter
25 // limit makes a miter join effectively beveled.
allowed_stroke(const SkStrokeRec & stroke,bool * isMiter)26 inline static bool allowed_stroke(const SkStrokeRec& stroke, bool* isMiter) {
27 SkASSERT(stroke.getStyle() == SkStrokeRec::kStroke_Style ||
28 stroke.getStyle() == SkStrokeRec::kHairline_Style);
29 // For hairlines, make bevel and round joins appear the same as mitered ones.
30 if (!stroke.getWidth()) {
31 *isMiter = true;
32 return true;
33 }
34 if (stroke.getJoin() == SkPaint::kBevel_Join) {
35 *isMiter = false;
36 return true;
37 }
38 if (stroke.getJoin() == SkPaint::kMiter_Join) {
39 *isMiter = stroke.getMiter() >= SK_ScalarSqrt2;
40 return true;
41 }
42 return false;
43 }
44
compute_rects(SkRect * devOutside,SkRect * devOutsideAssist,SkRect * devInside,bool * isDegenerate,const SkMatrix & viewMatrix,const SkRect & rect,SkScalar strokeWidth,bool miterStroke)45 static void compute_rects(SkRect* devOutside, SkRect* devOutsideAssist, SkRect* devInside,
46 bool* isDegenerate, const SkMatrix& viewMatrix, const SkRect& rect,
47 SkScalar strokeWidth, bool miterStroke) {
48 SkRect devRect;
49 viewMatrix.mapRect(&devRect, rect);
50
51 SkVector devStrokeSize;
52 if (strokeWidth > 0) {
53 devStrokeSize.set(strokeWidth, strokeWidth);
54 viewMatrix.mapVectors(&devStrokeSize, 1);
55 devStrokeSize.setAbs(devStrokeSize);
56 } else {
57 devStrokeSize.set(SK_Scalar1, SK_Scalar1);
58 }
59
60 const SkScalar dx = devStrokeSize.fX;
61 const SkScalar dy = devStrokeSize.fY;
62 const SkScalar rx = SkScalarHalf(dx);
63 const SkScalar ry = SkScalarHalf(dy);
64
65 *devOutside = devRect;
66 *devOutsideAssist = devRect;
67 *devInside = devRect;
68
69 devOutside->outset(rx, ry);
70 devInside->inset(rx, ry);
71
72 // If we have a degenerate stroking rect(ie the stroke is larger than inner rect) then we
73 // make a degenerate inside rect to avoid double hitting. We will also jam all of the points
74 // together when we render these rects.
75 SkScalar spare;
76 {
77 SkScalar w = devRect.width() - dx;
78 SkScalar h = devRect.height() - dy;
79 spare = SkTMin(w, h);
80 }
81
82 *isDegenerate = spare <= 0;
83 if (*isDegenerate) {
84 devInside->fLeft = devInside->fRight = devRect.centerX();
85 devInside->fTop = devInside->fBottom = devRect.centerY();
86 }
87
88 // For bevel-stroke, use 2 SkRect instances(devOutside and devOutsideAssist)
89 // to draw the outside of the octagon. Because there are 8 vertices on the outer
90 // edge, while vertex number of inner edge is 4, the same as miter-stroke.
91 if (!miterStroke) {
92 devOutside->inset(0, ry);
93 devOutsideAssist->outset(0, ry);
94 }
95 }
96
create_stroke_rect_gp(bool tweakAlphaForCoverage,const SkMatrix & viewMatrix,bool usesLocalCoords)97 static sk_sp<GrGeometryProcessor> create_stroke_rect_gp(bool tweakAlphaForCoverage,
98 const SkMatrix& viewMatrix,
99 bool usesLocalCoords) {
100 using namespace GrDefaultGeoProcFactory;
101
102 Coverage::Type coverageType;
103 if (tweakAlphaForCoverage) {
104 coverageType = Coverage::kSolid_Type;
105 } else {
106 coverageType = Coverage::kAttribute_Type;
107 }
108 LocalCoords::Type localCoordsType =
109 usesLocalCoords ? LocalCoords::kUsePosition_Type : LocalCoords::kUnused_Type;
110 return MakeForDeviceSpace(Color::kPremulGrColorAttribute_Type, coverageType, localCoordsType,
111 viewMatrix);
112 }
113
114 namespace {
115
116 class AAStrokeRectOp final : public GrMeshDrawOp {
117 private:
118 using Helper = GrSimpleMeshDrawOpHelper;
119
120 public:
121 DEFINE_OP_CLASS_ID
122
Make(GrPaint && paint,const SkMatrix & viewMatrix,const SkRect & devOutside,const SkRect & devInside)123 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
124 const SkRect& devOutside, const SkRect& devInside) {
125 return Helper::FactoryHelper<AAStrokeRectOp>(std::move(paint), viewMatrix, devOutside,
126 devInside);
127 }
128
AAStrokeRectOp(const Helper::MakeArgs & helperArgs,GrColor color,const SkMatrix & viewMatrix,const SkRect & devOutside,const SkRect & devInside)129 AAStrokeRectOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
130 const SkRect& devOutside, const SkRect& devInside)
131 : INHERITED(ClassID())
132 , fHelper(helperArgs, GrAAType::kCoverage)
133 , fViewMatrix(viewMatrix) {
134 SkASSERT(!devOutside.isEmpty());
135 SkASSERT(!devInside.isEmpty());
136
137 fRects.emplace_back(RectInfo{color, devOutside, devOutside, devInside, false});
138 this->setBounds(devOutside, HasAABloat::kYes, IsZeroArea::kNo);
139 fMiterStroke = true;
140 }
141
Make(GrPaint && paint,const SkMatrix & viewMatrix,const SkRect & rect,const SkStrokeRec & stroke)142 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
143 const SkRect& rect, const SkStrokeRec& stroke) {
144 bool isMiter;
145 if (!allowed_stroke(stroke, &isMiter)) {
146 return nullptr;
147 }
148 return Helper::FactoryHelper<AAStrokeRectOp>(std::move(paint), viewMatrix, rect, stroke,
149 isMiter);
150 }
151
AAStrokeRectOp(const Helper::MakeArgs & helperArgs,GrColor color,const SkMatrix & viewMatrix,const SkRect & rect,const SkStrokeRec & stroke,bool isMiter)152 AAStrokeRectOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
153 const SkRect& rect, const SkStrokeRec& stroke, bool isMiter)
154 : INHERITED(ClassID())
155 , fHelper(helperArgs, GrAAType::kCoverage)
156 , fViewMatrix(viewMatrix) {
157 fMiterStroke = isMiter;
158 RectInfo& info = fRects.push_back();
159 compute_rects(&info.fDevOutside, &info.fDevOutsideAssist, &info.fDevInside,
160 &info.fDegenerate, viewMatrix, rect, stroke.getWidth(), isMiter);
161 info.fColor = color;
162 if (isMiter) {
163 this->setBounds(info.fDevOutside, HasAABloat::kYes, IsZeroArea::kNo);
164 } else {
165 // The outer polygon of the bevel stroke is an octagon specified by the points of a
166 // pair of overlapping rectangles where one is wide and the other is narrow.
167 SkRect bounds = info.fDevOutside;
168 bounds.joinPossiblyEmptyRect(info.fDevOutsideAssist);
169 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
170 }
171 }
172
name() const173 const char* name() const override { return "AAStrokeRect"; }
174
visitProxies(const VisitProxyFunc & func) const175 void visitProxies(const VisitProxyFunc& func) const override {
176 fHelper.visitProxies(func);
177 }
178
dumpInfo() const179 SkString dumpInfo() const override {
180 SkString string;
181 for (const auto& info : fRects) {
182 string.appendf(
183 "Color: 0x%08x, ORect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
184 "AssistORect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
185 "IRect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], Degen: %d",
186 info.fColor, info.fDevOutside.fLeft, info.fDevOutside.fTop,
187 info.fDevOutside.fRight, info.fDevOutside.fBottom, info.fDevOutsideAssist.fLeft,
188 info.fDevOutsideAssist.fTop, info.fDevOutsideAssist.fRight,
189 info.fDevOutsideAssist.fBottom, info.fDevInside.fLeft, info.fDevInside.fTop,
190 info.fDevInside.fRight, info.fDevInside.fBottom, info.fDegenerate);
191 }
192 string += fHelper.dumpInfo();
193 string += INHERITED::dumpInfo();
194 return string;
195 }
196
fixedFunctionFlags() const197 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
198
finalize(const GrCaps & caps,const GrAppliedClip * clip,GrPixelConfigIsClamped dstIsClamped)199 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
200 GrPixelConfigIsClamped dstIsClamped) override {
201 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
202 GrProcessorAnalysisCoverage::kSingleChannel,
203 &fRects.back().fColor);
204 }
205
206 private:
207 void onPrepareDraws(Target*) override;
208
209 static const int kMiterIndexCnt = 3 * 24;
210 static const int kMiterVertexCnt = 16;
211 static const int kNumMiterRectsInIndexBuffer = 256;
212
213 static const int kBevelIndexCnt = 48 + 36 + 24;
214 static const int kBevelVertexCnt = 24;
215 static const int kNumBevelRectsInIndexBuffer = 256;
216
217 static sk_sp<const GrBuffer> GetIndexBuffer(GrResourceProvider*, bool miterStroke);
218
viewMatrix() const219 const SkMatrix& viewMatrix() const { return fViewMatrix; }
miterStroke() const220 bool miterStroke() const { return fMiterStroke; }
221
222 bool onCombineIfPossible(GrOp* t, const GrCaps&) override;
223
224 void generateAAStrokeRectGeometry(void* vertices,
225 size_t offset,
226 size_t vertexStride,
227 int outerVertexNum,
228 int innerVertexNum,
229 GrColor color,
230 const SkRect& devOutside,
231 const SkRect& devOutsideAssist,
232 const SkRect& devInside,
233 bool miterStroke,
234 bool degenerate,
235 bool tweakAlphaForCoverage) const;
236
237 // TODO support AA rotated stroke rects by copying around view matrices
238 struct RectInfo {
239 GrColor fColor;
240 SkRect fDevOutside;
241 SkRect fDevOutsideAssist;
242 SkRect fDevInside;
243 bool fDegenerate;
244 };
245
246 Helper fHelper;
247 SkSTArray<1, RectInfo, true> fRects;
248 SkMatrix fViewMatrix;
249 bool fMiterStroke;
250
251 typedef GrMeshDrawOp INHERITED;
252 };
253
254 } // anonymous namespace
255
onPrepareDraws(Target * target)256 void AAStrokeRectOp::onPrepareDraws(Target* target) {
257 sk_sp<GrGeometryProcessor> gp(create_stroke_rect_gp(fHelper.compatibleWithAlphaAsCoverage(),
258 this->viewMatrix(),
259 fHelper.usesLocalCoords()));
260 if (!gp) {
261 SkDebugf("Couldn't create GrGeometryProcessor\n");
262 return;
263 }
264
265 size_t vertexStride = gp->getVertexStride();
266
267 SkASSERT(fHelper.compatibleWithAlphaAsCoverage()
268 ? vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr)
269 : vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
270 int innerVertexNum = 4;
271 int outerVertexNum = this->miterStroke() ? 4 : 8;
272 int verticesPerInstance = (outerVertexNum + innerVertexNum) * 2;
273 int indicesPerInstance = this->miterStroke() ? kMiterIndexCnt : kBevelIndexCnt;
274 int instanceCount = fRects.count();
275
276 sk_sp<const GrBuffer> indexBuffer = GetIndexBuffer(target->resourceProvider(), this->miterStroke());
277 PatternHelper helper(GrPrimitiveType::kTriangles);
278 void* vertices =
279 helper.init(target, vertexStride, indexBuffer.get(),
280 verticesPerInstance, indicesPerInstance, instanceCount);
281 if (!vertices || !indexBuffer) {
282 SkDebugf("Could not allocate vertices\n");
283 return;
284 }
285
286 for (int i = 0; i < instanceCount; i++) {
287 const RectInfo& info = fRects[i];
288 this->generateAAStrokeRectGeometry(vertices,
289 i * verticesPerInstance * vertexStride,
290 vertexStride,
291 outerVertexNum,
292 innerVertexNum,
293 info.fColor,
294 info.fDevOutside,
295 info.fDevOutsideAssist,
296 info.fDevInside,
297 fMiterStroke,
298 info.fDegenerate,
299 fHelper.compatibleWithAlphaAsCoverage());
300 }
301 helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
302 }
303
GetIndexBuffer(GrResourceProvider * resourceProvider,bool miterStroke)304 sk_sp<const GrBuffer> AAStrokeRectOp::GetIndexBuffer(GrResourceProvider* resourceProvider,
305 bool miterStroke) {
306 if (miterStroke) {
307 // clang-format off
308 static const uint16_t gMiterIndices[] = {
309 0 + 0, 1 + 0, 5 + 0, 5 + 0, 4 + 0, 0 + 0,
310 1 + 0, 2 + 0, 6 + 0, 6 + 0, 5 + 0, 1 + 0,
311 2 + 0, 3 + 0, 7 + 0, 7 + 0, 6 + 0, 2 + 0,
312 3 + 0, 0 + 0, 4 + 0, 4 + 0, 7 + 0, 3 + 0,
313
314 0 + 4, 1 + 4, 5 + 4, 5 + 4, 4 + 4, 0 + 4,
315 1 + 4, 2 + 4, 6 + 4, 6 + 4, 5 + 4, 1 + 4,
316 2 + 4, 3 + 4, 7 + 4, 7 + 4, 6 + 4, 2 + 4,
317 3 + 4, 0 + 4, 4 + 4, 4 + 4, 7 + 4, 3 + 4,
318
319 0 + 8, 1 + 8, 5 + 8, 5 + 8, 4 + 8, 0 + 8,
320 1 + 8, 2 + 8, 6 + 8, 6 + 8, 5 + 8, 1 + 8,
321 2 + 8, 3 + 8, 7 + 8, 7 + 8, 6 + 8, 2 + 8,
322 3 + 8, 0 + 8, 4 + 8, 4 + 8, 7 + 8, 3 + 8,
323 };
324 // clang-format on
325 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gMiterIndices) == kMiterIndexCnt);
326 GR_DEFINE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
327 return resourceProvider->findOrCreatePatternedIndexBuffer(
328 gMiterIndices, kMiterIndexCnt, kNumMiterRectsInIndexBuffer, kMiterVertexCnt,
329 gMiterIndexBufferKey);
330 } else {
331 /**
332 * As in miter-stroke, index = a + b, and a is the current index, b is the shift
333 * from the first index. The index layout:
334 * outer AA line: 0~3, 4~7
335 * outer edge: 8~11, 12~15
336 * inner edge: 16~19
337 * inner AA line: 20~23
338 * Following comes a bevel-stroke rect and its indices:
339 *
340 * 4 7
341 * *********************************
342 * * ______________________________ *
343 * * / 12 15 \ *
344 * * / \ *
345 * 0 * |8 16_____________________19 11 | * 3
346 * * | | | | *
347 * * | | **************** | | *
348 * * | | * 20 23 * | | *
349 * * | | * * | | *
350 * * | | * 21 22 * | | *
351 * * | | **************** | | *
352 * * | |____________________| | *
353 * 1 * |9 17 18 10| * 2
354 * * \ / *
355 * * \13 __________________________14/ *
356 * * *
357 * **********************************
358 * 5 6
359 */
360 // clang-format off
361 static const uint16_t gBevelIndices[] = {
362 // Draw outer AA, from outer AA line to outer edge, shift is 0.
363 0 + 0, 1 + 0, 9 + 0, 9 + 0, 8 + 0, 0 + 0,
364 1 + 0, 5 + 0, 13 + 0, 13 + 0, 9 + 0, 1 + 0,
365 5 + 0, 6 + 0, 14 + 0, 14 + 0, 13 + 0, 5 + 0,
366 6 + 0, 2 + 0, 10 + 0, 10 + 0, 14 + 0, 6 + 0,
367 2 + 0, 3 + 0, 11 + 0, 11 + 0, 10 + 0, 2 + 0,
368 3 + 0, 7 + 0, 15 + 0, 15 + 0, 11 + 0, 3 + 0,
369 7 + 0, 4 + 0, 12 + 0, 12 + 0, 15 + 0, 7 + 0,
370 4 + 0, 0 + 0, 8 + 0, 8 + 0, 12 + 0, 4 + 0,
371
372 // Draw the stroke, from outer edge to inner edge, shift is 8.
373 0 + 8, 1 + 8, 9 + 8, 9 + 8, 8 + 8, 0 + 8,
374 1 + 8, 5 + 8, 9 + 8,
375 5 + 8, 6 + 8, 10 + 8, 10 + 8, 9 + 8, 5 + 8,
376 6 + 8, 2 + 8, 10 + 8,
377 2 + 8, 3 + 8, 11 + 8, 11 + 8, 10 + 8, 2 + 8,
378 3 + 8, 7 + 8, 11 + 8,
379 7 + 8, 4 + 8, 8 + 8, 8 + 8, 11 + 8, 7 + 8,
380 4 + 8, 0 + 8, 8 + 8,
381
382 // Draw the inner AA, from inner edge to inner AA line, shift is 16.
383 0 + 16, 1 + 16, 5 + 16, 5 + 16, 4 + 16, 0 + 16,
384 1 + 16, 2 + 16, 6 + 16, 6 + 16, 5 + 16, 1 + 16,
385 2 + 16, 3 + 16, 7 + 16, 7 + 16, 6 + 16, 2 + 16,
386 3 + 16, 0 + 16, 4 + 16, 4 + 16, 7 + 16, 3 + 16,
387 };
388 // clang-format on
389 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gBevelIndices) == kBevelIndexCnt);
390
391 GR_DEFINE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
392 return resourceProvider->findOrCreatePatternedIndexBuffer(
393 gBevelIndices, kBevelIndexCnt, kNumBevelRectsInIndexBuffer, kBevelVertexCnt,
394 gBevelIndexBufferKey);
395 }
396 }
397
onCombineIfPossible(GrOp * t,const GrCaps & caps)398 bool AAStrokeRectOp::onCombineIfPossible(GrOp* t, const GrCaps& caps) {
399 AAStrokeRectOp* that = t->cast<AAStrokeRectOp>();
400
401 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
402 return false;
403 }
404
405 // TODO combine across miterstroke changes
406 if (this->miterStroke() != that->miterStroke()) {
407 return false;
408 }
409
410 // We apply the viewmatrix to the rect points on the cpu. However, if the pipeline uses
411 // local coords then we won't be able to combine. TODO: Upload local coords as an attribute.
412 if (fHelper.usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
413 return false;
414 }
415
416 fRects.push_back_n(that->fRects.count(), that->fRects.begin());
417 this->joinBounds(*that);
418 return true;
419 }
420
setup_scale(int * scale,SkScalar inset)421 static void setup_scale(int* scale, SkScalar inset) {
422 if (inset < SK_ScalarHalf) {
423 *scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf));
424 SkASSERT(*scale >= 0 && *scale <= 255);
425 } else {
426 *scale = 0xff;
427 }
428 }
429
generateAAStrokeRectGeometry(void * vertices,size_t offset,size_t vertexStride,int outerVertexNum,int innerVertexNum,GrColor color,const SkRect & devOutside,const SkRect & devOutsideAssist,const SkRect & devInside,bool miterStroke,bool degenerate,bool tweakAlphaForCoverage) const430 void AAStrokeRectOp::generateAAStrokeRectGeometry(void* vertices,
431 size_t offset,
432 size_t vertexStride,
433 int outerVertexNum,
434 int innerVertexNum,
435 GrColor color,
436 const SkRect& devOutside,
437 const SkRect& devOutsideAssist,
438 const SkRect& devInside,
439 bool miterStroke,
440 bool degenerate,
441 bool tweakAlphaForCoverage) const {
442 intptr_t verts = reinterpret_cast<intptr_t>(vertices) + offset;
443
444 // We create vertices for four nested rectangles. There are two ramps from 0 to full
445 // coverage, one on the exterior of the stroke and the other on the interior.
446 // The following pointers refer to the four rects, from outermost to innermost.
447 SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts);
448 SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + outerVertexNum * vertexStride);
449 SkPoint* fan2Pos = reinterpret_cast<SkPoint*>(verts + 2 * outerVertexNum * vertexStride);
450 SkPoint* fan3Pos = reinterpret_cast<SkPoint*>(
451 verts + (2 * outerVertexNum + innerVertexNum) * vertexStride);
452
453 #ifndef SK_IGNORE_THIN_STROKED_RECT_FIX
454 // TODO: this only really works if the X & Y margins are the same all around
455 // the rect (or if they are all >= 1.0).
456 SkScalar inset;
457 if (!degenerate) {
458 inset = SkMinScalar(SK_Scalar1, devOutside.fRight - devInside.fRight);
459 inset = SkMinScalar(inset, devInside.fLeft - devOutside.fLeft);
460 inset = SkMinScalar(inset, devInside.fTop - devOutside.fTop);
461 if (miterStroke) {
462 inset = SK_ScalarHalf * SkMinScalar(inset, devOutside.fBottom - devInside.fBottom);
463 } else {
464 inset = SK_ScalarHalf *
465 SkMinScalar(inset, devOutsideAssist.fBottom - devInside.fBottom);
466 }
467 SkASSERT(inset >= 0);
468 } else {
469 // TODO use real devRect here
470 inset = SkMinScalar(devOutside.width(), SK_Scalar1);
471 inset = SK_ScalarHalf *
472 SkMinScalar(inset, SkTMax(devOutside.height(), devOutsideAssist.height()));
473 }
474 #else
475 SkScalar inset;
476 if (!degenerate) {
477 inset = SK_ScalarHalf;
478 } else {
479 // TODO use real devRect here
480 inset = SkMinScalar(devOutside.width(), SK_Scalar1);
481 inset = SK_ScalarHalf *
482 SkMinScalar(inset, SkTMax(devOutside.height(), devOutsideAssist.height()));
483 }
484 #endif
485
486 if (miterStroke) {
487 // outermost
488 set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
489 // inner two
490 set_inset_fan(fan1Pos, vertexStride, devOutside, inset, inset);
491 if (!degenerate) {
492 set_inset_fan(fan2Pos, vertexStride, devInside, -inset, -inset);
493 // innermost
494 set_inset_fan(fan3Pos, vertexStride, devInside, SK_ScalarHalf, SK_ScalarHalf);
495 } else {
496 // When the interior rect has become degenerate we smoosh to a single point
497 SkASSERT(devInside.fLeft == devInside.fRight && devInside.fTop == devInside.fBottom);
498 SkPointPriv::SetRectFan(fan2Pos, devInside.fLeft, devInside.fTop, devInside.fRight,
499 devInside.fBottom, vertexStride);
500 SkPointPriv::SetRectFan(fan3Pos, devInside.fLeft, devInside.fTop, devInside.fRight,
501 devInside.fBottom, vertexStride);
502 }
503 } else {
504 SkPoint* fan0AssistPos = reinterpret_cast<SkPoint*>(verts + 4 * vertexStride);
505 SkPoint* fan1AssistPos =
506 reinterpret_cast<SkPoint*>(verts + (outerVertexNum + 4) * vertexStride);
507 // outermost
508 set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
509 set_inset_fan(fan0AssistPos, vertexStride, devOutsideAssist, -SK_ScalarHalf,
510 -SK_ScalarHalf);
511 // outer one of the inner two
512 set_inset_fan(fan1Pos, vertexStride, devOutside, inset, inset);
513 set_inset_fan(fan1AssistPos, vertexStride, devOutsideAssist, inset, inset);
514 if (!degenerate) {
515 // inner one of the inner two
516 set_inset_fan(fan2Pos, vertexStride, devInside, -inset, -inset);
517 // innermost
518 set_inset_fan(fan3Pos, vertexStride, devInside, SK_ScalarHalf, SK_ScalarHalf);
519 } else {
520 // When the interior rect has become degenerate we smoosh to a single point
521 SkASSERT(devInside.fLeft == devInside.fRight && devInside.fTop == devInside.fBottom);
522 SkPointPriv::SetRectFan(fan2Pos, devInside.fLeft, devInside.fTop, devInside.fRight,
523 devInside.fBottom, vertexStride);
524 SkPointPriv::SetRectFan(fan3Pos, devInside.fLeft, devInside.fTop, devInside.fRight,
525 devInside.fBottom, vertexStride);
526 }
527 }
528
529 // Make verts point to vertex color and then set all the color and coverage vertex attrs
530 // values. The outermost rect has 0 coverage
531 verts += sizeof(SkPoint);
532 for (int i = 0; i < outerVertexNum; ++i) {
533 if (tweakAlphaForCoverage) {
534 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0;
535 } else {
536 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
537 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = 0;
538 }
539 }
540
541 // scale is the coverage for the the inner two rects.
542 int scale;
543 setup_scale(&scale, inset);
544
545 float innerCoverage = GrNormalizeByteToFloat(scale);
546 GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
547
548 verts += outerVertexNum * vertexStride;
549 for (int i = 0; i < outerVertexNum + innerVertexNum; ++i) {
550 if (tweakAlphaForCoverage) {
551 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
552 } else {
553 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
554 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = innerCoverage;
555 }
556 }
557
558 // The innermost rect has 0 coverage, unless we are degenerate, in which case we must apply the
559 // scaled coverage
560 verts += (outerVertexNum + innerVertexNum) * vertexStride;
561 if (!degenerate) {
562 innerCoverage = 0;
563 scaledColor = 0;
564 }
565
566 for (int i = 0; i < innerVertexNum; ++i) {
567 if (tweakAlphaForCoverage) {
568 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
569 } else {
570 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
571 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = innerCoverage;
572 }
573 }
574 }
575
576 namespace GrRectOpFactory {
577
MakeAAFillNestedRects(GrPaint && paint,const SkMatrix & viewMatrix,const SkRect rects[2])578 std::unique_ptr<GrDrawOp> MakeAAFillNestedRects(GrPaint&& paint,
579 const SkMatrix& viewMatrix,
580 const SkRect rects[2]) {
581 SkASSERT(viewMatrix.rectStaysRect());
582 SkASSERT(!rects[0].isEmpty() && !rects[1].isEmpty());
583
584 SkRect devOutside, devInside;
585 viewMatrix.mapRect(&devOutside, rects[0]);
586 viewMatrix.mapRect(&devInside, rects[1]);
587 if (devInside.isEmpty()) {
588 if (devOutside.isEmpty()) {
589 return nullptr;
590 }
591 return MakeAAFill(std::move(paint), viewMatrix, rects[0]);
592 }
593
594 return AAStrokeRectOp::Make(std::move(paint), viewMatrix, devOutside, devInside);
595 }
596
MakeAAStroke(GrPaint && paint,const SkMatrix & viewMatrix,const SkRect & rect,const SkStrokeRec & stroke)597 std::unique_ptr<GrDrawOp> MakeAAStroke(GrPaint&& paint,
598 const SkMatrix& viewMatrix,
599 const SkRect& rect,
600 const SkStrokeRec& stroke) {
601 return AAStrokeRectOp::Make(std::move(paint), viewMatrix, rect, stroke);
602 }
603
604 } // namespace GrRectOpFactory
605
606 ///////////////////////////////////////////////////////////////////////////////////////////////////
607
608 #if GR_TEST_UTILS
609
610 #include "GrDrawOpTest.h"
611
GR_DRAW_OP_TEST_DEFINE(AAStrokeRectOp)612 GR_DRAW_OP_TEST_DEFINE(AAStrokeRectOp) {
613 bool miterStroke = random->nextBool();
614
615 // Create either a empty rect or a non-empty rect.
616 SkRect rect =
617 random->nextBool() ? SkRect::MakeXYWH(10, 10, 50, 40) : SkRect::MakeXYWH(6, 7, 0, 0);
618 SkScalar minDim = SkMinScalar(rect.width(), rect.height());
619 SkScalar strokeWidth = random->nextUScalar1() * minDim;
620
621 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
622 rec.setStrokeStyle(strokeWidth);
623 rec.setStrokeParams(SkPaint::kButt_Cap,
624 miterStroke ? SkPaint::kMiter_Join : SkPaint::kBevel_Join, 1.f);
625 SkMatrix matrix = GrTest::TestMatrixRectStaysRect(random);
626 return GrRectOpFactory::MakeAAStroke(std::move(paint), matrix, rect, rec);
627 }
628
629 #endif
630