/* * 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 "GrColor.h" #include "GrDefaultGeoProcFactory.h" #include "GrMeshDrawOp.h" #include "GrOpFlushState.h" #include "GrRectOpFactory.h" #include "GrResourceKey.h" #include "GrResourceProvider.h" #include "GrTypes.h" #include "SkMatrix.h" #include "SkMatrixPriv.h" #include "SkRect.h" #include "SkPointPriv.h" #include "ops/GrSimpleMeshDrawOpHelper.h" GR_DECLARE_STATIC_UNIQUE_KEY(gAAFillRectIndexBufferKey); static inline bool view_matrix_ok_for_aa_fill_rect(const SkMatrix& viewMatrix) { return viewMatrix.preservesRightAngles(); } static inline void set_inset_fan(SkPoint* pts, size_t stride, const SkRect& r, SkScalar dx, SkScalar dy) { SkPointPriv::SetRectFan(pts, 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; static sk_sp<const GrBuffer> get_index_buffer(GrResourceProvider* resourceProvider) { GR_DEFINE_STATIC_UNIQUE_KEY(gAAFillRectIndexBufferKey); // clang-format off 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, }; // clang-format on GR_STATIC_ASSERT(SK_ARRAY_COUNT(gFillAARectIdx) == kIndicesPerAAFillRect); return resourceProvider->findOrCreatePatternedIndexBuffer( 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, bool tweakAlphaForCoverage, 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 SkPointPriv::SetRectFan(fan0Pos, rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, vertexStride); SkMatrixPriv::MapPointsWithStride(viewMatrix, 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)); SkMatrixPriv::MapPointsWithStride(localCoordMatrix, fan0Loc, vertexStride, fan0Pos, vertexStride, 8); } // 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; } } } namespace { class AAFillRectOp final : public GrMeshDrawOp { private: using Helper = GrSimpleMeshDrawOpHelperWithStencil; public: DEFINE_OP_CLASS_ID static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix, const SkRect& rect, const SkRect& devRect, const SkMatrix* localMatrix, const GrUserStencilSettings* stencil) { SkASSERT(view_matrix_ok_for_aa_fill_rect(viewMatrix)); return Helper::FactoryHelper<AAFillRectOp>(std::move(paint), viewMatrix, rect, devRect, localMatrix, stencil); } AAFillRectOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix, const SkRect& rect, const SkRect& devRect, const SkMatrix* localMatrix, const GrUserStencilSettings* stencil) : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage, stencil) { 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 "AAFillRectOp"; } void visitProxies(const VisitProxyFunc& func) const override { fHelper.visitProxies(func); } SkString dumpInfo() const override { SkString str; str.append(INHERITED::dumpInfo()); str.appendf("# combined: %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 += fHelper.dumpInfo(); str += INHERITED::dumpInfo(); return str; } FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); } RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip, GrPixelConfigIsClamped dstIsClamped) override { GrColor color = this->first()->color(); auto result = fHelper.xpRequiresDstTexture( caps, clip, dstIsClamped, GrProcessorAnalysisCoverage::kSingleChannel, &color); this->first()->setColor(color); return result; } private: void onPrepareDraws(Target* target) override { using namespace GrDefaultGeoProcFactory; Color color(Color::kPremulGrColorAttribute_Type); Coverage::Type coverageType = fHelper.compatibleWithAlphaAsCoverage() ? Coverage::kSolid_Type : Coverage::kAttribute_Type; LocalCoords lc = fHelper.usesLocalCoords() ? LocalCoords::kHasExplicit_Type : LocalCoords::kUnused_Type; sk_sp<GrGeometryProcessor> gp = GrDefaultGeoProcFactory::Make(color, coverageType, lc, SkMatrix::I()); if (!gp) { SkDebugf("Couldn't create GrGeometryProcessor\n"); return; } size_t vertexStride = gp->getVertexStride(); sk_sp<const GrBuffer> indexBuffer = get_index_buffer(target->resourceProvider()); PatternHelper helper(GrPrimitiveType::kTriangles); void* vertices = helper.init(target, vertexStride, indexBuffer.get(), 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 (fHelper.usesLocalCoords()) { 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(), fHelper.compatibleWithAlphaAsCoverage(), localMatrix); info = this->next(info); } helper.recordDraw(target, gp.get(), fHelper.makePipeline(target)); } bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override { AAFillRectOp* that = t->cast<AAFillRectOp>(); if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) { return false; } 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); } SkSTArray<4 * sizeof(RectWithLocalMatrixInfo), uint8_t, true> fRectData; Helper fHelper; int fRectCnt; typedef GrMeshDrawOp INHERITED; }; } // anonymous namespace namespace GrRectOpFactory { std::unique_ptr<GrDrawOp> MakeAAFill(GrPaint&& paint, const SkMatrix& viewMatrix, const SkRect& rect, const GrUserStencilSettings* stencil) { if (!view_matrix_ok_for_aa_fill_rect(viewMatrix)) { return nullptr; } SkRect devRect; viewMatrix.mapRect(&devRect, rect); return AAFillRectOp::Make(std::move(paint), viewMatrix, rect, devRect, nullptr, stencil); } std::unique_ptr<GrDrawOp> MakeAAFillWithLocalMatrix(GrPaint&& paint, const SkMatrix& viewMatrix, const SkMatrix& localMatrix, const SkRect& rect) { if (!view_matrix_ok_for_aa_fill_rect(viewMatrix)) { return nullptr; } SkRect devRect; viewMatrix.mapRect(&devRect, rect); return AAFillRectOp::Make(std::move(paint), viewMatrix, rect, devRect, &localMatrix, nullptr); } std::unique_ptr<GrDrawOp> MakeAAFillWithLocalRect(GrPaint&& paint, const SkMatrix& viewMatrix, const SkRect& rect, const SkRect& localRect) { if (!view_matrix_ok_for_aa_fill_rect(viewMatrix)) { return nullptr; } SkRect devRect; viewMatrix.mapRect(&devRect, rect); SkMatrix localMatrix; if (!localMatrix.setRectToRect(rect, localRect, SkMatrix::kFill_ScaleToFit)) { return nullptr; } return AAFillRectOp::Make(std::move(paint), viewMatrix, rect, devRect, &localMatrix, nullptr); } } // namespace GrRectOpFactory /////////////////////////////////////////////////////////////////////////////////////////////////// #if GR_TEST_UTILS #include "GrDrawOpTest.h" GR_DRAW_OP_TEST_DEFINE(AAFillRectOp) { SkMatrix viewMatrix; do { viewMatrix = GrTest::TestMatrixInvertible(random); } while (!view_matrix_ok_for_aa_fill_rect(viewMatrix)); SkRect rect = GrTest::TestRect(random); SkRect devRect; viewMatrix.mapRect(&devRect, rect); const SkMatrix* localMatrix = nullptr; SkMatrix m; if (random->nextBool()) { m = GrTest::TestMatrix(random); } const GrUserStencilSettings* stencil = random->nextBool() ? nullptr : GrGetRandomStencil(random, context); return AAFillRectOp::Make(std::move(paint), viewMatrix, rect, devRect, localMatrix, stencil); } #endif