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