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 "GrAAFillRectOp.h"
9
10 #include "GrColor.h"
11 #include "GrDefaultGeoProcFactory.h"
12 #include "GrMeshDrawOp.h"
13 #include "GrOpFlushState.h"
14 #include "GrResourceKey.h"
15 #include "GrResourceProvider.h"
16 #include "GrTypes.h"
17 #include "SkMatrix.h"
18 #include "SkRect.h"
19
20 GR_DECLARE_STATIC_UNIQUE_KEY(gAAFillRectIndexBufferKey);
21
set_inset_fan(SkPoint * pts,size_t stride,const SkRect & r,SkScalar dx,SkScalar dy)22 static void set_inset_fan(SkPoint* pts, size_t stride, const SkRect& r, SkScalar dx, SkScalar dy) {
23 pts->setRectFan(r.fLeft + dx, r.fTop + dy, r.fRight - dx, r.fBottom - dy, stride);
24 }
25
26 static const int kNumAAFillRectsInIndexBuffer = 256;
27 static const int kVertsPerAAFillRect = 8;
28 static const int kIndicesPerAAFillRect = 30;
29
get_index_buffer(GrResourceProvider * resourceProvider)30 const GrBuffer* get_index_buffer(GrResourceProvider* resourceProvider) {
31 GR_DEFINE_STATIC_UNIQUE_KEY(gAAFillRectIndexBufferKey);
32
33 // clang-format off
34 static const uint16_t gFillAARectIdx[] = {
35 0, 1, 5, 5, 4, 0,
36 1, 2, 6, 6, 5, 1,
37 2, 3, 7, 7, 6, 2,
38 3, 0, 4, 4, 7, 3,
39 4, 5, 6, 6, 7, 4,
40 };
41 // clang-format on
42
43 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gFillAARectIdx) == kIndicesPerAAFillRect);
44 return resourceProvider->findOrCreateInstancedIndexBuffer(
45 gFillAARectIdx, kIndicesPerAAFillRect, kNumAAFillRectsInIndexBuffer,
46 kVertsPerAAFillRect, gAAFillRectIndexBufferKey);
47 }
48
generate_aa_fill_rect_geometry(intptr_t verts,size_t vertexStride,GrColor color,const SkMatrix & viewMatrix,const SkRect & rect,const SkRect & devRect,bool tweakAlphaForCoverage,const SkMatrix * localMatrix)49 static void generate_aa_fill_rect_geometry(intptr_t verts,
50 size_t vertexStride,
51 GrColor color,
52 const SkMatrix& viewMatrix,
53 const SkRect& rect,
54 const SkRect& devRect,
55 bool tweakAlphaForCoverage,
56 const SkMatrix* localMatrix) {
57 SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts);
58 SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + 4 * vertexStride);
59
60 SkScalar inset;
61
62 if (viewMatrix.rectStaysRect()) {
63 inset = SkMinScalar(devRect.width(), SK_Scalar1);
64 inset = SK_ScalarHalf * SkMinScalar(inset, devRect.height());
65
66 set_inset_fan(fan0Pos, vertexStride, devRect, -SK_ScalarHalf, -SK_ScalarHalf);
67 set_inset_fan(fan1Pos, vertexStride, devRect, inset, inset);
68 } else {
69 // compute transformed (1, 0) and (0, 1) vectors
70 SkVector vec[2] = {{viewMatrix[SkMatrix::kMScaleX], viewMatrix[SkMatrix::kMSkewY]},
71 {viewMatrix[SkMatrix::kMSkewX], viewMatrix[SkMatrix::kMScaleY]}};
72
73 SkScalar len1 = SkPoint::Normalize(&vec[0]);
74 vec[0].scale(SK_ScalarHalf);
75 SkScalar len2 = SkPoint::Normalize(&vec[1]);
76 vec[1].scale(SK_ScalarHalf);
77
78 inset = SkMinScalar(len1 * rect.width(), SK_Scalar1);
79 inset = SK_ScalarHalf * SkMinScalar(inset, len2 * rect.height());
80
81 // create the rotated rect
82 fan0Pos->setRectFan(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, vertexStride);
83 viewMatrix.mapPointsWithStride(fan0Pos, vertexStride, 4);
84
85 // Now create the inset points and then outset the original
86 // rotated points
87
88 // TL
89 *((SkPoint*)((intptr_t)fan1Pos + 0 * vertexStride)) =
90 *((SkPoint*)((intptr_t)fan0Pos + 0 * vertexStride)) + vec[0] + vec[1];
91 *((SkPoint*)((intptr_t)fan0Pos + 0 * vertexStride)) -= vec[0] + vec[1];
92 // BL
93 *((SkPoint*)((intptr_t)fan1Pos + 1 * vertexStride)) =
94 *((SkPoint*)((intptr_t)fan0Pos + 1 * vertexStride)) + vec[0] - vec[1];
95 *((SkPoint*)((intptr_t)fan0Pos + 1 * vertexStride)) -= vec[0] - vec[1];
96 // BR
97 *((SkPoint*)((intptr_t)fan1Pos + 2 * vertexStride)) =
98 *((SkPoint*)((intptr_t)fan0Pos + 2 * vertexStride)) - vec[0] - vec[1];
99 *((SkPoint*)((intptr_t)fan0Pos + 2 * vertexStride)) += vec[0] + vec[1];
100 // TR
101 *((SkPoint*)((intptr_t)fan1Pos + 3 * vertexStride)) =
102 *((SkPoint*)((intptr_t)fan0Pos + 3 * vertexStride)) - vec[0] + vec[1];
103 *((SkPoint*)((intptr_t)fan0Pos + 3 * vertexStride)) += vec[0] - vec[1];
104 }
105
106 if (localMatrix) {
107 SkMatrix invViewMatrix;
108 if (!viewMatrix.invert(&invViewMatrix)) {
109 SkDebugf("View matrix is non-invertible, local coords will be wrong.");
110 invViewMatrix = SkMatrix::I();
111 }
112 SkMatrix localCoordMatrix;
113 localCoordMatrix.setConcat(*localMatrix, invViewMatrix);
114 SkPoint* fan0Loc = reinterpret_cast<SkPoint*>(verts + sizeof(SkPoint) + sizeof(GrColor));
115 localCoordMatrix.mapPointsWithStride(fan0Loc, fan0Pos, vertexStride, 8);
116 }
117
118 // Make verts point to vertex color and then set all the color and coverage vertex attrs
119 // values.
120 verts += sizeof(SkPoint);
121
122 // The coverage offset is always the last vertex attribute
123 intptr_t coverageOffset = vertexStride - sizeof(GrColor) - sizeof(SkPoint);
124 for (int i = 0; i < 4; ++i) {
125 if (tweakAlphaForCoverage) {
126 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0;
127 } else {
128 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
129 *reinterpret_cast<float*>(verts + i * vertexStride + coverageOffset) = 0;
130 }
131 }
132
133 int scale;
134 if (inset < SK_ScalarHalf) {
135 scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf));
136 SkASSERT(scale >= 0 && scale <= 255);
137 } else {
138 scale = 0xff;
139 }
140
141 verts += 4 * vertexStride;
142
143 float innerCoverage = GrNormalizeByteToFloat(scale);
144 GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
145
146 for (int i = 0; i < 4; ++i) {
147 if (tweakAlphaForCoverage) {
148 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
149 } else {
150 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
151 *reinterpret_cast<float*>(verts + i * vertexStride + coverageOffset) = innerCoverage;
152 }
153 }
154 }
155
156 class AAFillRectOp final: public GrMeshDrawOp {
157 public:
158 DEFINE_OP_CLASS_ID
159
AAFillRectOp(GrColor color,const SkMatrix & viewMatrix,const SkRect & rect,const SkRect & devRect,const SkMatrix * localMatrix)160 AAFillRectOp(GrColor color,
161 const SkMatrix& viewMatrix,
162 const SkRect& rect,
163 const SkRect& devRect,
164 const SkMatrix* localMatrix)
165 : INHERITED(ClassID()) {
166 if (localMatrix) {
167 void* mem = fRectData.push_back_n(sizeof(RectWithLocalMatrixInfo));
168 new (mem) RectWithLocalMatrixInfo(color, viewMatrix, rect, devRect, *localMatrix);
169 } else {
170 void* mem = fRectData.push_back_n(sizeof(RectInfo));
171 new (mem) RectInfo(color, viewMatrix, rect, devRect);
172 }
173 IsZeroArea zeroArea =
174 (!rect.width() || !rect.height()) ? IsZeroArea::kYes : IsZeroArea::kNo;
175 this->setBounds(devRect, HasAABloat::kYes, zeroArea);
176 fRectCnt = 1;
177 }
178
name() const179 const char* name() const override { return "AAFillRectOp"; }
180
dumpInfo() const181 SkString dumpInfo() const override {
182 SkString str;
183 str.appendf("# combined: %d\n", fRectCnt);
184 const RectInfo* info = this->first();
185 for (int i = 0; i < fRectCnt; ++i) {
186 const SkRect& rect = info->rect();
187 str.appendf("%d: Color: 0x%08x, Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n", i,
188 info->color(), rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
189 info = this->next(info);
190 }
191 str.append(DumpPipelineInfo(*this->pipeline()));
192 str.append(INHERITED::dumpInfo());
193 return str;
194 }
195
applyPipelineOptimizations(const GrPipelineOptimizations & optimizations)196 void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
197 GrColor color;
198 if (optimizations.getOverrideColorIfSet(&color)) {
199 this->first()->setColor(color);
200 }
201 fCanTweakAlphaForCoverage = optimizations.canTweakAlphaForCoverage();
202 fNeedsLocalCoords = optimizations.readsLocalCoords();
203 }
204
205 private:
getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor * color,GrPipelineAnalysisCoverage * coverage) const206 void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
207 GrPipelineAnalysisCoverage* coverage) const override {
208 color->setToConstant(this->first()->color());
209 *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
210 }
211
onPrepareDraws(Target * target) const212 void onPrepareDraws(Target* target) const override {
213 using namespace GrDefaultGeoProcFactory;
214
215 Color color(Color::kPremulGrColorAttribute_Type);
216 Coverage::Type coverageType;
217 if (fCanTweakAlphaForCoverage) {
218 coverageType = Coverage::kSolid_Type;
219 } else {
220 coverageType = Coverage::kAttribute_Type;
221 }
222 LocalCoords lc =
223 fNeedsLocalCoords ? LocalCoords::kHasExplicit_Type : LocalCoords::kUnused_Type;
224 sk_sp<GrGeometryProcessor> gp =
225 GrDefaultGeoProcFactory::Make(color, coverageType, lc, SkMatrix::I());
226 if (!gp) {
227 SkDebugf("Couldn't create GrGeometryProcessor\n");
228 return;
229 }
230
231 size_t vertexStride = gp->getVertexStride();
232
233 sk_sp<const GrBuffer> indexBuffer(get_index_buffer(target->resourceProvider()));
234 InstancedHelper helper;
235 void* vertices =
236 helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer.get(),
237 kVertsPerAAFillRect, kIndicesPerAAFillRect, fRectCnt);
238 if (!vertices || !indexBuffer) {
239 SkDebugf("Could not allocate vertices\n");
240 return;
241 }
242
243 const RectInfo* info = this->first();
244 const SkMatrix* localMatrix = nullptr;
245 for (int i = 0; i < fRectCnt; i++) {
246 intptr_t verts =
247 reinterpret_cast<intptr_t>(vertices) + i * kVertsPerAAFillRect * vertexStride;
248 if (fNeedsLocalCoords) {
249 if (info->hasLocalMatrix()) {
250 localMatrix = &static_cast<const RectWithLocalMatrixInfo*>(info)->localMatrix();
251 } else {
252 localMatrix = &SkMatrix::I();
253 }
254 }
255 generate_aa_fill_rect_geometry(verts, vertexStride, info->color(), info->viewMatrix(),
256 info->rect(), info->devRect(), fCanTweakAlphaForCoverage,
257 localMatrix);
258 info = this->next(info);
259 }
260 helper.recordDraw(target, gp.get());
261 }
262
onCombineIfPossible(GrOp * t,const GrCaps & caps)263 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
264 AAFillRectOp* that = t->cast<AAFillRectOp>();
265 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
266 that->bounds(), caps)) {
267 return false;
268 }
269
270 SkASSERT(fNeedsLocalCoords == that->fNeedsLocalCoords);
271
272 // In the event of two ops, one who can tweak, one who cannot, we just fall back to not
273 // tweaking.
274 if (fCanTweakAlphaForCoverage && !that->fCanTweakAlphaForCoverage) {
275 fCanTweakAlphaForCoverage = false;
276 }
277
278 fRectData.push_back_n(that->fRectData.count(), that->fRectData.begin());
279 fRectCnt += that->fRectCnt;
280 this->joinBounds(*that);
281 return true;
282 }
283
284 struct RectInfo {
285 public:
RectInfoAAFillRectOp::RectInfo286 RectInfo(GrColor color, const SkMatrix& viewMatrix, const SkRect& rect,
287 const SkRect& devRect)
288 : RectInfo(color, viewMatrix, rect, devRect, HasLocalMatrix::kNo) {}
hasLocalMatrixAAFillRectOp::RectInfo289 bool hasLocalMatrix() const { return HasLocalMatrix::kYes == fHasLocalMatrix; }
colorAAFillRectOp::RectInfo290 GrColor color() const { return fColor; }
viewMatrixAAFillRectOp::RectInfo291 const SkMatrix& viewMatrix() const { return fViewMatrix; }
rectAAFillRectOp::RectInfo292 const SkRect& rect() const { return fRect; }
devRectAAFillRectOp::RectInfo293 const SkRect& devRect() const { return fDevRect; }
294
setColorAAFillRectOp::RectInfo295 void setColor(GrColor color) { fColor = color; }
296
297 protected:
298 enum class HasLocalMatrix : uint32_t { kNo, kYes };
299
RectInfoAAFillRectOp::RectInfo300 RectInfo(GrColor color, const SkMatrix& viewMatrix, const SkRect& rect,
301 const SkRect& devRect, HasLocalMatrix hasLM)
302 : fHasLocalMatrix(hasLM)
303 , fColor(color)
304 , fViewMatrix(viewMatrix)
305 , fRect(rect)
306 , fDevRect(devRect) {}
307
308 HasLocalMatrix fHasLocalMatrix;
309 GrColor fColor;
310 SkMatrix fViewMatrix;
311 SkRect fRect;
312 SkRect fDevRect;
313 };
314
315 struct RectWithLocalMatrixInfo : public RectInfo {
316 public:
RectWithLocalMatrixInfoAAFillRectOp::RectWithLocalMatrixInfo317 RectWithLocalMatrixInfo(GrColor color, const SkMatrix& viewMatrix, const SkRect& rect,
318 const SkRect& devRect, const SkMatrix& localMatrix)
319 : RectInfo(color, viewMatrix, rect, devRect, HasLocalMatrix::kYes)
320 , fLocalMatrix(localMatrix) {}
localMatrixAAFillRectOp::RectWithLocalMatrixInfo321 const SkMatrix& localMatrix() const { return fLocalMatrix; }
322
323 private:
324 SkMatrix fLocalMatrix;
325 };
326
first()327 RectInfo* first() { return reinterpret_cast<RectInfo*>(fRectData.begin()); }
first() const328 const RectInfo* first() const { return reinterpret_cast<const RectInfo*>(fRectData.begin()); }
next(const RectInfo * prev) const329 const RectInfo* next(const RectInfo* prev) const {
330 intptr_t next =
331 reinterpret_cast<intptr_t>(prev) +
332 (prev->hasLocalMatrix() ? sizeof(RectWithLocalMatrixInfo) : sizeof(RectInfo));
333 return reinterpret_cast<const RectInfo*>(next);
334 }
335
336 bool fNeedsLocalCoords;
337 bool fCanTweakAlphaForCoverage;
338 SkSTArray<4 * sizeof(RectWithLocalMatrixInfo), uint8_t, true> fRectData;
339 int fRectCnt;
340
341 typedef GrMeshDrawOp INHERITED;
342 };
343
344 namespace GrAAFillRectOp {
345
Make(GrColor color,const SkMatrix & viewMatrix,const SkRect & rect,const SkRect & devRect)346 std::unique_ptr<GrMeshDrawOp> Make(GrColor color,
347 const SkMatrix& viewMatrix,
348 const SkRect& rect,
349 const SkRect& devRect) {
350 return std::unique_ptr<GrMeshDrawOp>(
351 new AAFillRectOp(color, viewMatrix, rect, devRect, nullptr));
352 }
353
Make(GrColor color,const SkMatrix & viewMatrix,const SkMatrix & localMatrix,const SkRect & rect,const SkRect & devRect)354 std::unique_ptr<GrMeshDrawOp> Make(GrColor color,
355 const SkMatrix& viewMatrix,
356 const SkMatrix& localMatrix,
357 const SkRect& rect,
358 const SkRect& devRect) {
359 return std::unique_ptr<GrMeshDrawOp>(
360 new AAFillRectOp(color, viewMatrix, rect, devRect, &localMatrix));
361 }
362
Make(GrColor color,const SkMatrix & viewMatrix,const SkMatrix & localMatrix,const SkRect & rect)363 std::unique_ptr<GrMeshDrawOp> Make(GrColor color,
364 const SkMatrix& viewMatrix,
365 const SkMatrix& localMatrix,
366 const SkRect& rect) {
367 SkRect devRect;
368 viewMatrix.mapRect(&devRect, rect);
369 return Make(color, viewMatrix, localMatrix, rect, devRect);
370 }
371
MakeWithLocalRect(GrColor color,const SkMatrix & viewMatrix,const SkRect & rect,const SkRect & localRect)372 std::unique_ptr<GrMeshDrawOp> MakeWithLocalRect(GrColor color,
373 const SkMatrix& viewMatrix,
374 const SkRect& rect,
375 const SkRect& localRect) {
376 SkRect devRect;
377 viewMatrix.mapRect(&devRect, rect);
378 SkMatrix localMatrix;
379 if (!localMatrix.setRectToRect(rect, localRect, SkMatrix::kFill_ScaleToFit)) {
380 return nullptr;
381 }
382 return Make(color, viewMatrix, localMatrix, rect, devRect);
383 }
384 };
385
386 ///////////////////////////////////////////////////////////////////////////////////////////////////
387
388 #if GR_TEST_UTILS
389
390 #include "GrDrawOpTest.h"
391
DRAW_OP_TEST_DEFINE(AAFillRectOp)392 DRAW_OP_TEST_DEFINE(AAFillRectOp) {
393 GrColor color = GrRandomColor(random);
394 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
395 SkRect rect = GrTest::TestRect(random);
396 SkRect devRect = GrTest::TestRect(random);
397 return GrAAFillRectOp::Make(color, viewMatrix, rect, devRect);
398 }
399
DRAW_OP_TEST_DEFINE(AAFillRectOpLocalMatrix)400 DRAW_OP_TEST_DEFINE(AAFillRectOpLocalMatrix) {
401 GrColor color = GrRandomColor(random);
402 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
403 SkMatrix localMatrix = GrTest::TestMatrix(random);
404 SkRect rect = GrTest::TestRect(random);
405 SkRect devRect = GrTest::TestRect(random);
406 return GrAAFillRectOp::Make(color, viewMatrix, localMatrix, rect, devRect);
407 }
408
409 #endif
410