blob: 4f93adf07429c5d7a0c930f1fa477339886f80d1 [file] [log] [blame]
/*
* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "GrAAFillRectBatch.h"
#include "GrBatchFlushState.h"
#include "GrColor.h"
#include "GrDefaultGeoProcFactory.h"
#include "GrResourceKey.h"
#include "GrResourceProvider.h"
#include "GrTypes.h"
#include "GrVertexBatch.h"
#include "SkMatrix.h"
#include "SkRect.h"
GR_DECLARE_STATIC_UNIQUE_KEY(gAAFillRectIndexBufferKey);
static void set_inset_fan(SkPoint* pts, size_t stride,
const SkRect& r, SkScalar dx, SkScalar dy) {
pts->setRectFan(r.fLeft + dx, r.fTop + dy,
r.fRight - dx, r.fBottom - dy, stride);
}
static const int kNumAAFillRectsInIndexBuffer = 256;
static const int kVertsPerAAFillRect = 8;
static const int kIndicesPerAAFillRect = 30;
const GrBuffer* get_index_buffer(GrResourceProvider* resourceProvider) {
GR_DEFINE_STATIC_UNIQUE_KEY(gAAFillRectIndexBufferKey);
static const uint16_t gFillAARectIdx[] = {
0, 1, 5, 5, 4, 0,
1, 2, 6, 6, 5, 1,
2, 3, 7, 7, 6, 2,
3, 0, 4, 4, 7, 3,
4, 5, 6, 6, 7, 4,
};
GR_STATIC_ASSERT(SK_ARRAY_COUNT(gFillAARectIdx) == kIndicesPerAAFillRect);
return resourceProvider->findOrCreateInstancedIndexBuffer(gFillAARectIdx,
kIndicesPerAAFillRect, kNumAAFillRectsInIndexBuffer, kVertsPerAAFillRect,
gAAFillRectIndexBufferKey);
}
static void generate_aa_fill_rect_geometry(intptr_t verts,
size_t vertexStride,
GrColor color,
const SkMatrix& viewMatrix,
const SkRect& rect,
const SkRect& devRect,
const GrXPOverridesForBatch& overrides,
const SkMatrix* localMatrix) {
SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts);
SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + 4 * vertexStride);
SkScalar inset;
if (viewMatrix.rectStaysRect()) {
inset = SkMinScalar(devRect.width(), SK_Scalar1);
inset = SK_ScalarHalf * SkMinScalar(inset, devRect.height());
set_inset_fan(fan0Pos, vertexStride, devRect, -SK_ScalarHalf, -SK_ScalarHalf);
set_inset_fan(fan1Pos, vertexStride, devRect, inset, inset);
} else {
// compute transformed (1, 0) and (0, 1) vectors
SkVector vec[2] = {
{ viewMatrix[SkMatrix::kMScaleX], viewMatrix[SkMatrix::kMSkewY] },
{ viewMatrix[SkMatrix::kMSkewX], viewMatrix[SkMatrix::kMScaleY] }
};
SkScalar len1 = SkPoint::Normalize(&vec[0]);
vec[0].scale(SK_ScalarHalf);
SkScalar len2 = SkPoint::Normalize(&vec[1]);
vec[1].scale(SK_ScalarHalf);
inset = SkMinScalar(len1 * rect.width(), SK_Scalar1);
inset = SK_ScalarHalf * SkMinScalar(inset, len2 * rect.height());
// create the rotated rect
fan0Pos->setRectFan(rect.fLeft, rect.fTop,
rect.fRight, rect.fBottom, vertexStride);
viewMatrix.mapPointsWithStride(fan0Pos, vertexStride, 4);
// Now create the inset points and then outset the original
// rotated points
// TL
*((SkPoint*)((intptr_t)fan1Pos + 0 * vertexStride)) =
*((SkPoint*)((intptr_t)fan0Pos + 0 * vertexStride)) + vec[0] + vec[1];
*((SkPoint*)((intptr_t)fan0Pos + 0 * vertexStride)) -= vec[0] + vec[1];
// BL
*((SkPoint*)((intptr_t)fan1Pos + 1 * vertexStride)) =
*((SkPoint*)((intptr_t)fan0Pos + 1 * vertexStride)) + vec[0] - vec[1];
*((SkPoint*)((intptr_t)fan0Pos + 1 * vertexStride)) -= vec[0] - vec[1];
// BR
*((SkPoint*)((intptr_t)fan1Pos + 2 * vertexStride)) =
*((SkPoint*)((intptr_t)fan0Pos + 2 * vertexStride)) - vec[0] - vec[1];
*((SkPoint*)((intptr_t)fan0Pos + 2 * vertexStride)) += vec[0] + vec[1];
// TR
*((SkPoint*)((intptr_t)fan1Pos + 3 * vertexStride)) =
*((SkPoint*)((intptr_t)fan0Pos + 3 * vertexStride)) - vec[0] + vec[1];
*((SkPoint*)((intptr_t)fan0Pos + 3 * vertexStride)) += vec[0] - vec[1];
}
if (localMatrix) {
SkMatrix invViewMatrix;
if (!viewMatrix.invert(&invViewMatrix)) {
SkDebugf("View matrix is non-invertible, local coords will be wrong.");
invViewMatrix = SkMatrix::I();
}
SkMatrix localCoordMatrix;
localCoordMatrix.setConcat(*localMatrix, invViewMatrix);
SkPoint* fan0Loc = reinterpret_cast<SkPoint*>(verts + sizeof(SkPoint) + sizeof(GrColor));
localCoordMatrix.mapPointsWithStride(fan0Loc, fan0Pos, vertexStride, 8);
}
bool tweakAlphaForCoverage = overrides.canTweakAlphaForCoverage();
// Make verts point to vertex color and then set all the color and coverage vertex attrs
// values.
verts += sizeof(SkPoint);
// The coverage offset is always the last vertex attribute
intptr_t coverageOffset = vertexStride - sizeof(GrColor) - sizeof(SkPoint);
for (int i = 0; i < 4; ++i) {
if (tweakAlphaForCoverage) {
*reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0;
} else {
*reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
*reinterpret_cast<float*>(verts + i * vertexStride + coverageOffset) = 0;
}
}
int scale;
if (inset < SK_ScalarHalf) {
scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf));
SkASSERT(scale >= 0 && scale <= 255);
} else {
scale = 0xff;
}
verts += 4 * vertexStride;
float innerCoverage = GrNormalizeByteToFloat(scale);
GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
for (int i = 0; i < 4; ++i) {
if (tweakAlphaForCoverage) {
*reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
} else {
*reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
*reinterpret_cast<float*>(verts + i * vertexStride +
coverageOffset) = innerCoverage;
}
}
}
class AAFillRectBatch : public GrVertexBatch {
public:
DEFINE_BATCH_CLASS_ID
AAFillRectBatch(GrColor color,
const SkMatrix& viewMatrix,
const SkRect& rect,
const SkRect& devRect,
const SkMatrix* localMatrix) : INHERITED(ClassID()) {
if (localMatrix) {
void* mem = fRectData.push_back_n(sizeof(RectWithLocalMatrixInfo));
new (mem) RectWithLocalMatrixInfo(color, viewMatrix, rect, devRect, *localMatrix);
} else {
void* mem = fRectData.push_back_n(sizeof(RectInfo));
new (mem) RectInfo(color, viewMatrix, rect, devRect);
}
IsZeroArea zeroArea = (!rect.width() || !rect.height()) ? IsZeroArea::kYes
: IsZeroArea::kNo;
this->setBounds(devRect, HasAABloat::kYes, zeroArea);
fRectCnt = 1;
}
const char* name() const override { return "AAFillRectBatch"; }
SkString dumpInfo() const override {
SkString str;
str.appendf("# batched: %d\n", fRectCnt);
const RectInfo* info = this->first();
for (int i = 0; i < fRectCnt; ++i) {
const SkRect& rect = info->rect();
str.appendf("%d: Color: 0x%08x, Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n",
i, info->color(), rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
info = this->next(info);
}
str.append(INHERITED::dumpInfo());
return str;
}
void computePipelineOptimizations(GrInitInvariantOutput* color,
GrInitInvariantOutput* coverage,
GrBatchToXPOverrides* overrides) const override {
// When this is called on a batch, there is only one rect
color->setKnownFourComponents(this->first()->color());
coverage->setUnknownSingleComponent();
}
void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
GrColor color;
if (overrides.getOverrideColorIfSet(&color)) {
this->first()->setColor(color);
}
fOverrides = overrides;
}
private:
void onPrepareDraws(Target* target) const override {
bool needLocalCoords = fOverrides.readsLocalCoords();
using namespace GrDefaultGeoProcFactory;
Color color(Color::kAttribute_Type);
Coverage::Type coverageType;
if (fOverrides.canTweakAlphaForCoverage()) {
coverageType = Coverage::kSolid_Type;
} else {
coverageType = Coverage::kAttribute_Type;
}
Coverage coverage(coverageType);
LocalCoords lc = needLocalCoords ? LocalCoords::kHasExplicit_Type
: LocalCoords::kUnused_Type;
sk_sp<GrGeometryProcessor> gp = GrDefaultGeoProcFactory::Make(color, coverage, lc,
SkMatrix::I());
if (!gp) {
SkDebugf("Couldn't create GrGeometryProcessor\n");
return;
}
size_t vertexStride = gp->getVertexStride();
SkAutoTUnref<const GrBuffer> indexBuffer(get_index_buffer(target->resourceProvider()));
InstancedHelper helper;
void* vertices = helper.init(target, kTriangles_GrPrimitiveType, vertexStride,
indexBuffer, kVertsPerAAFillRect,
kIndicesPerAAFillRect, fRectCnt);
if (!vertices || !indexBuffer) {
SkDebugf("Could not allocate vertices\n");
return;
}
const RectInfo* info = this->first();
const SkMatrix* localMatrix = nullptr;
for (int i = 0; i < fRectCnt; i++) {
intptr_t verts = reinterpret_cast<intptr_t>(vertices) +
i * kVertsPerAAFillRect * vertexStride;
if (needLocalCoords) {
if (info->hasLocalMatrix()) {
localMatrix = &static_cast<const RectWithLocalMatrixInfo*>(info)->localMatrix();
} else {
localMatrix = &SkMatrix::I();
}
}
generate_aa_fill_rect_geometry(verts, vertexStride, info->color(),
info->viewMatrix(), info->rect(),
info->devRect(), fOverrides, localMatrix);
info = this->next(info);
}
helper.recordDraw(target, gp.get());
}
bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
AAFillRectBatch* that = t->cast<AAFillRectBatch>();
if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
that->bounds(), caps)) {
return false;
}
// In the event of two batches, one who can tweak, one who cannot, we just fall back to
// not tweaking
if (fOverrides.canTweakAlphaForCoverage() && !that->fOverrides.canTweakAlphaForCoverage()) {
fOverrides = that->fOverrides;
}
fRectData.push_back_n(that->fRectData.count(), that->fRectData.begin());
fRectCnt += that->fRectCnt;
this->joinBounds(*that);
return true;
}
struct RectInfo {
public:
RectInfo(GrColor color, const SkMatrix& viewMatrix, const SkRect& rect,
const SkRect& devRect)
: RectInfo(color, viewMatrix, rect, devRect, HasLocalMatrix::kNo) {}
bool hasLocalMatrix() const { return HasLocalMatrix::kYes == fHasLocalMatrix; }
GrColor color() const { return fColor; }
const SkMatrix& viewMatrix() const { return fViewMatrix; }
const SkRect& rect() const { return fRect; }
const SkRect& devRect() const { return fDevRect; }
void setColor(GrColor color) { fColor = color; }
protected:
enum class HasLocalMatrix : uint32_t { kNo, kYes };
RectInfo(GrColor color, const SkMatrix& viewMatrix, const SkRect& rect,
const SkRect& devRect, HasLocalMatrix hasLM)
: fHasLocalMatrix(hasLM)
, fColor(color)
, fViewMatrix(viewMatrix)
, fRect(rect)
, fDevRect(devRect) {}
HasLocalMatrix fHasLocalMatrix;
GrColor fColor;
SkMatrix fViewMatrix;
SkRect fRect;
SkRect fDevRect;
};
struct RectWithLocalMatrixInfo : public RectInfo {
public:
RectWithLocalMatrixInfo(GrColor color, const SkMatrix& viewMatrix, const SkRect& rect,
const SkRect& devRect, const SkMatrix& localMatrix)
: RectInfo(color, viewMatrix, rect, devRect, HasLocalMatrix::kYes)
, fLocalMatrix(localMatrix) {}
const SkMatrix& localMatrix() const { return fLocalMatrix; }
private:
SkMatrix fLocalMatrix;
};
RectInfo* first() { return reinterpret_cast<RectInfo*>(fRectData.begin()); }
const RectInfo* first() const { return reinterpret_cast<const RectInfo*>(fRectData.begin()); }
const RectInfo* next(const RectInfo* prev) const {
intptr_t next = reinterpret_cast<intptr_t>(prev) +
(prev->hasLocalMatrix() ? sizeof(RectWithLocalMatrixInfo)
: sizeof(RectInfo));
return reinterpret_cast<const RectInfo*>(next);
}
GrXPOverridesForBatch fOverrides;
SkSTArray<4 * sizeof(RectWithLocalMatrixInfo), uint8_t, true> fRectData;
int fRectCnt;
typedef GrVertexBatch INHERITED;
};
namespace GrAAFillRectBatch {
GrDrawBatch* Create(GrColor color,
const SkMatrix& viewMatrix,
const SkRect& rect,
const SkRect& devRect) {
return new AAFillRectBatch(color, viewMatrix, rect, devRect, nullptr);
}
GrDrawBatch* Create(GrColor color,
const SkMatrix& viewMatrix,
const SkMatrix& localMatrix,
const SkRect& rect,
const SkRect& devRect) {
return new AAFillRectBatch(color, viewMatrix, rect, devRect, &localMatrix);
}
GrDrawBatch* Create(GrColor color,
const SkMatrix& viewMatrix,
const SkMatrix& localMatrix,
const SkRect& rect) {
SkRect devRect;
viewMatrix.mapRect(&devRect, rect);
return Create(color, viewMatrix, localMatrix, rect, devRect);
}
GrDrawBatch* CreateWithLocalRect(GrColor color,
const SkMatrix& viewMatrix,
const SkRect& rect,
const SkRect& localRect) {
SkRect devRect;
viewMatrix.mapRect(&devRect, rect);
SkMatrix localMatrix;
if (!localMatrix.setRectToRect(rect, localRect, SkMatrix::kFill_ScaleToFit)) {
return nullptr;
}
return Create(color, viewMatrix, localMatrix, rect, devRect);
}
};
///////////////////////////////////////////////////////////////////////////////////////////////////
#ifdef GR_TEST_UTILS
#include "GrBatchTest.h"
DRAW_BATCH_TEST_DEFINE(AAFillRectBatch) {
GrColor color = GrRandomColor(random);
SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
SkRect rect = GrTest::TestRect(random);
SkRect devRect = GrTest::TestRect(random);
return GrAAFillRectBatch::Create(color, viewMatrix, rect, devRect);
}
DRAW_BATCH_TEST_DEFINE(AAFillRectBatchLocalMatrix) {
GrColor color = GrRandomColor(random);
SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
SkMatrix localMatrix = GrTest::TestMatrix(random);
SkRect rect = GrTest::TestRect(random);
SkRect devRect = GrTest::TestRect(random);
return GrAAFillRectBatch::Create(color, viewMatrix, localMatrix, rect, devRect);
}
#endif