diff --git a/gn/core.gni b/gn/core.gni index da92020cfc..1d88821f7d 100644 --- a/gn/core.gni +++ b/gn/core.gni @@ -121,6 +121,7 @@ skia_core_sources = [ "$_src/core/SkDither.h", "$_src/core/SkDocument.cpp", "$_src/core/SkDraw.cpp", + "$_src/core/SkDraw_vertices.cpp", "$_src/core/SkDraw.h", "$_src/core/SkDrawable.cpp", "$_src/core/SkDrawLooper.cpp", diff --git a/src/core/SkAutoBlitterChoose.h b/src/core/SkAutoBlitterChoose.h new file mode 100644 index 0000000000..460e30d076 --- /dev/null +++ b/src/core/SkAutoBlitterChoose.h @@ -0,0 +1,46 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkAutoBlitterChoose_DEFINED +#define SkAutoBlitterChoose_DEFINED + +#include "SkArenaAlloc.h" +#include "SkBlitter.h" + +class SkMatrix; +class SkPaint; +class SkPixmap; + +class SkAutoBlitterChoose : SkNoncopyable { +public: + SkAutoBlitterChoose() { + fBlitter = nullptr; + } + SkAutoBlitterChoose(const SkPixmap& dst, const SkMatrix& matrix, + const SkPaint& paint, bool drawCoverage = false) { + fBlitter = SkBlitter::Choose(dst, matrix, paint, &fAlloc, drawCoverage); + } + + SkBlitter* operator->() { return fBlitter; } + SkBlitter* get() const { return fBlitter; } + + void choose(const SkPixmap& dst, const SkMatrix& matrix, + const SkPaint& paint, bool drawCoverage = false) { + SkASSERT(!fBlitter); + fBlitter = SkBlitter::Choose(dst, matrix, paint, &fAlloc, drawCoverage); + } + +private: + // Owned by fAlloc, which will handle the delete. + SkBlitter* fBlitter; + + char fStorage[kSkBlitterContextSize]; + SkArenaAlloc fAlloc{fStorage}; +}; +#define SkAutoBlitterChoose(...) SK_REQUIRE_LOCAL_VAR(SkAutoBlitterChoose) + +#endif diff --git a/src/core/SkDraw.cpp b/src/core/SkDraw.cpp index cbb9d26d14..8245d46093 100644 --- a/src/core/SkDraw.cpp +++ b/src/core/SkDraw.cpp @@ -4,23 +4,23 @@ * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ + #define __STDC_LIMIT_MACROS -#include "SkDraw.h" - #include "SkArenaAlloc.h" +#include "SkAutoBlitterChoose.h" #include "SkBlendModePriv.h" #include "SkBlitter.h" #include "SkCanvas.h" #include "SkColorPriv.h" -#include "SkColorShader.h" #include "SkDevice.h" #include "SkDeviceLooper.h" +#include "SkDraw.h" +#include "SkDrawProcs.h" #include "SkFindAndPlaceGlyph.h" -#include "SkFixed.h" -#include "SkLocalMatrixShader.h" #include "SkMaskFilter.h" #include "SkMatrix.h" +#include "SkMatrixUtils.h" #include "SkPaint.h" #include "SkPathEffect.h" #include "SkRasterClip.h" @@ -34,50 +34,12 @@ #include "SkTemplates.h" #include "SkTextMapStateProc.h" #include "SkTLazy.h" -#include "SkUnPreMultiply.h" #include "SkUtils.h" -#include "SkVertState.h" - -#include "SkBitmapProcShader.h" -#include "SkDrawProcs.h" -#include "SkMatrixUtils.h" - -//#define TRACE_BITMAP_DRAWS // Helper function to fix code gen bug on ARM64. // See SkFindAndPlaceGlyph.h for more details. void FixGCC49Arm64Bug(int v) { } -/** Helper for allocating small blitters on the stack. - */ -class SkAutoBlitterChoose : SkNoncopyable { -public: - SkAutoBlitterChoose() { - fBlitter = nullptr; - } - SkAutoBlitterChoose(const SkPixmap& dst, const SkMatrix& matrix, - const SkPaint& paint, bool drawCoverage = false) { - fBlitter = SkBlitter::Choose(dst, matrix, paint, &fAlloc, drawCoverage); - } - - SkBlitter* operator->() { return fBlitter; } - SkBlitter* get() const { return fBlitter; } - - void choose(const SkPixmap& dst, const SkMatrix& matrix, - const SkPaint& paint, bool drawCoverage = false) { - SkASSERT(!fBlitter); - fBlitter = SkBlitter::Choose(dst, matrix, paint, &fAlloc, drawCoverage); - } - -private: - // Owned by fAlloc, which will handle the delete. - SkBlitter* fBlitter; - - char fStorage[kSkBlitterContextSize]; - SkArenaAlloc fAlloc{fStorage}; -}; -#define SkAutoBlitterChoose(...) SK_REQUIRE_LOCAL_VAR(SkAutoBlitterChoose) - static SkPaint make_paint_with_image( const SkPaint& origPaint, const SkBitmap& bitmap, SkMatrix* matrix = nullptr) { SkPaint paint(origPaint); @@ -1670,470 +1632,7 @@ void SkDraw::drawPosText(const char text[], size_t byteLength, const SkScalar po #pragma warning ( pop ) #endif -/////////////////////////////////////////////////////////////////////////////// - -struct Matrix43 { - float fMat[12]; // column major - - Sk4f map(float x, float y) const { - return Sk4f::Load(&fMat[0]) * x + Sk4f::Load(&fMat[4]) * y + Sk4f::Load(&fMat[8]); - } - - void setConcat(const Matrix43& a, const SkMatrix& b) { - fMat[ 0] = a.dot(0, b.getScaleX(), b.getSkewY()); - fMat[ 1] = a.dot(1, b.getScaleX(), b.getSkewY()); - fMat[ 2] = a.dot(2, b.getScaleX(), b.getSkewY()); - fMat[ 3] = a.dot(3, b.getScaleX(), b.getSkewY()); - - fMat[ 4] = a.dot(0, b.getSkewX(), b.getScaleY()); - fMat[ 5] = a.dot(1, b.getSkewX(), b.getScaleY()); - fMat[ 6] = a.dot(2, b.getSkewX(), b.getScaleY()); - fMat[ 7] = a.dot(3, b.getSkewX(), b.getScaleY()); - - fMat[ 8] = a.dot(0, b.getTranslateX(), b.getTranslateY()) + a.fMat[ 8]; - fMat[ 9] = a.dot(1, b.getTranslateX(), b.getTranslateY()) + a.fMat[ 9]; - fMat[10] = a.dot(2, b.getTranslateX(), b.getTranslateY()) + a.fMat[10]; - fMat[11] = a.dot(3, b.getTranslateX(), b.getTranslateY()) + a.fMat[11]; - } - -private: - float dot(int index, float x, float y) const { - return fMat[index + 0] * x + fMat[index + 4] * y; - } -}; - -static SkScan::HairRCProc ChooseHairProc(bool doAntiAlias) { - return doAntiAlias ? SkScan::AntiHairLine : SkScan::HairLine; -} - -static bool texture_to_matrix(const VertState& state, const SkPoint verts[], - const SkPoint texs[], SkMatrix* matrix) { - SkPoint src[3], dst[3]; - - src[0] = texs[state.f0]; - src[1] = texs[state.f1]; - src[2] = texs[state.f2]; - dst[0] = verts[state.f0]; - dst[1] = verts[state.f1]; - dst[2] = verts[state.f2]; - return matrix->setPolyToPoly(src, dst, 3); -} - -class SkTriColorShader : public SkShader { -public: - SkTriColorShader(); - - class TriColorShaderContext : public SkShader::Context { - public: - TriColorShaderContext(const SkTriColorShader& shader, const ContextRec&); - ~TriColorShaderContext() override; - void shadeSpan(int x, int y, SkPMColor dstC[], int count) override; - void shadeSpan4f(int x, int y, SkPM4f dstC[], int count) override; - - private: - bool setup(const SkPoint pts[], const SkColor colors[], int, int, int); - - SkMatrix fDstToUnit; - SkPMColor fColors[3]; - bool fSetup; - - Matrix43 fM43; - - typedef SkShader::Context INHERITED; - }; - - struct TriColorShaderData { - const SkPoint* pts; - const SkColor* colors; - const VertState *state; - }; - - SK_TO_STRING_OVERRIDE() - - // For serialization. This will never be called. - Factory getFactory() const override { sk_throw(); return nullptr; } - - // Supply setup data to context from drawing setup - void bindSetupData(TriColorShaderData* setupData) { fSetupData = setupData; } - - // Take the setup data from context when needed. - TriColorShaderData* takeSetupData() { - TriColorShaderData *data = fSetupData; - fSetupData = NULL; - return data; - } - -protected: - Context* onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc) const override { - return alloc->make(*this, rec); - } - -private: - TriColorShaderData *fSetupData; - - typedef SkShader INHERITED; -}; - -bool SkTriColorShader::TriColorShaderContext::setup(const SkPoint pts[], const SkColor colors[], - int index0, int index1, int index2) { - - fColors[0] = SkPreMultiplyColor(colors[index0]); - fColors[1] = SkPreMultiplyColor(colors[index1]); - fColors[2] = SkPreMultiplyColor(colors[index2]); - - SkMatrix m, im; - m.reset(); - m.set(0, pts[index1].fX - pts[index0].fX); - m.set(1, pts[index2].fX - pts[index0].fX); - m.set(2, pts[index0].fX); - m.set(3, pts[index1].fY - pts[index0].fY); - m.set(4, pts[index2].fY - pts[index0].fY); - m.set(5, pts[index0].fY); - if (!m.invert(&im)) { - return false; - } - // We can't call getTotalInverse(), because we explicitly don't want to look at the localmatrix - // as our interators are intrinsically tied to the vertices, and nothing else. - SkMatrix ctmInv; - if (!this->getCTM().invert(&ctmInv)) { - return false; - } - // TODO replace INV(m) * INV(ctm) with INV(ctm * m) - fDstToUnit.setConcat(im, ctmInv); - - Sk4f alpha(this->getPaintAlpha() * (1.0f / 255)), - c0 = SkPM4f::FromPMColor(fColors[0]).to4f() * alpha, - c1 = SkPM4f::FromPMColor(fColors[1]).to4f() * alpha, - c2 = SkPM4f::FromPMColor(fColors[2]).to4f() * alpha; - - Matrix43 colorm; - (c1 - c0).store(&colorm.fMat[0]); - (c2 - c0).store(&colorm.fMat[4]); - c0.store(&colorm.fMat[8]); - fM43.setConcat(colorm, fDstToUnit); - - return true; -} - -#include "SkColorPriv.h" -#include "SkComposeShader.h" - -static int ScalarTo256(SkScalar v) { - return static_cast(SkScalarPin(v, 0, 1) * 256 + 0.5); -} - -SkTriColorShader::SkTriColorShader() - : INHERITED(NULL) - , fSetupData(NULL) {} - -SkTriColorShader::TriColorShaderContext::TriColorShaderContext(const SkTriColorShader& shader, - const ContextRec& rec) - : INHERITED(shader, rec) - , fSetup(false) {} - -SkTriColorShader::TriColorShaderContext::~TriColorShaderContext() {} - -void SkTriColorShader::TriColorShaderContext::shadeSpan(int x, int y, SkPMColor dstC[], int count) { - SkTriColorShader* parent = static_cast(const_cast(&fShader)); - TriColorShaderData* set = parent->takeSetupData(); - if (set) { - fSetup = setup(set->pts, set->colors, set->state->f0, set->state->f1, set->state->f2); - } - - if (!fSetup) { - // Invalid matrices. Not checked before so no need to assert. - return; - } - - const int alphaScale = Sk255To256(this->getPaintAlpha()); - - SkPoint src; - - fDstToUnit.mapXY(SkIntToScalar(x) + 0.5, SkIntToScalar(y) + 0.5, &src); - for (int i = 0; i < count; i++) { - int scale1 = ScalarTo256(src.fX); - int scale2 = ScalarTo256(src.fY); - int scale0 = 256 - scale1 - scale2; - if (scale0 < 0) { - if (scale1 > scale2) { - scale2 = 256 - scale1; - } else { - scale1 = 256 - scale2; - } - scale0 = 0; - } - - if (256 != alphaScale) { - scale0 = SkAlphaMul(scale0, alphaScale); - scale1 = SkAlphaMul(scale1, alphaScale); - scale2 = SkAlphaMul(scale2, alphaScale); - } - - dstC[i] = SkAlphaMulQ(fColors[0], scale0) + - SkAlphaMulQ(fColors[1], scale1) + - SkAlphaMulQ(fColors[2], scale2); - - src.fX += fDstToUnit.getScaleX(); - src.fY += fDstToUnit.getSkewY(); - } -} - -void SkTriColorShader::TriColorShaderContext::shadeSpan4f(int x, int y, SkPM4f dstC[], int count) { - SkTriColorShader* parent = static_cast(const_cast(&fShader)); - TriColorShaderData* set = parent->takeSetupData(); - if (set) { - fSetup = setup(set->pts, set->colors, set->state->f0, set->state->f1, set->state->f2); - } - - if (!fSetup) { - // Invalid matrices. Not checked before so no need to assert. - return; - } - - Sk4f c = fM43.map(SkIntToScalar(x) + 0.5, SkIntToScalar(y) + 0.5), - dc = Sk4f::Load(&fM43.fMat[0]), - zero(0.0f), - one(1.0f); - - for (int i = 0; i < count; i++) { - // We don't expect to be wildly out of 0...1, but we pin just because of minor - // numerical imprecision. - Sk4f::Min(Sk4f::Max(c, zero), Sk4f::Min(c[3], one)).store(dstC + i); - c += dc; - } -} - -#ifndef SK_IGNORE_TO_STRING -void SkTriColorShader::toString(SkString* str) const { - str->append("SkTriColorShader: ("); - - this->INHERITED::toString(str); - - str->append(")"); -} -#endif - -namespace { - -// Similar to SkLocalMatrixShader, but composes the local matrix with the CTM (instead -// of composing with the inherited local matrix): -// -// rec' = {rec.ctm x localMatrix, rec.localMatrix} -// -// (as opposed to rec' = {rec.ctm, rec.localMatrix x localMatrix}) -// -class SkLocalInnerMatrixShader final : public SkShader { -public: - SkLocalInnerMatrixShader(sk_sp proxy, const SkMatrix& localMatrix) - : INHERITED(&localMatrix) - , fProxyShader(std::move(proxy)) {} - - Factory getFactory() const override { - SkASSERT(false); - return nullptr; - } - -protected: - void flatten(SkWriteBuffer&) const override { - SkASSERT(false); - } - - Context* onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc) const override { - SkMatrix adjustedCTM = SkMatrix::Concat(*rec.fMatrix, this->getLocalMatrix()); - ContextRec newRec(rec); - newRec.fMatrix = &adjustedCTM; - return fProxyShader->makeContext(newRec, alloc); - } - - bool onAppendStages(SkRasterPipeline* p, SkColorSpace* cs, SkArenaAlloc* alloc, - const SkMatrix& ctm, const SkPaint& paint, - const SkMatrix* localM) const override { - // We control the shader graph ancestors, so we know there's no local matrix being - // injected before this. - SkASSERT(!localM); - - SkMatrix adjustedCTM = SkMatrix::Concat(ctm, this->getLocalMatrix()); - return fProxyShader->appendStages(p, cs, alloc, adjustedCTM, paint); - } - -private: - sk_sp fProxyShader; - - typedef SkShader INHERITED; -}; - -sk_sp MakeTextureShader(const VertState& state, const SkPoint verts[], - const SkPoint texs[], const SkPaint& paint, - SkColorSpace* dstColorSpace, - SkArenaAlloc* alloc) { - SkASSERT(paint.getShader()); - - const auto& p0 = texs[state.f0], - p1 = texs[state.f1], - p2 = texs[state.f2]; - - if (p0 != p1 || p0 != p2) { - // Common case (non-collapsed texture coordinates). - // Map the texture to vertices using a local transform. - - // We cannot use a plain SkLocalMatrix shader, because we need the texture matrix - // to compose next to the CTM. - SkMatrix localMatrix; - return texture_to_matrix(state, verts, texs, &localMatrix) - ? alloc->makeSkSp(paint.refShader(), localMatrix) - : nullptr; - } - - // Collapsed texture coordinates special case. - // The texture is a solid color, sampled at the given point. - SkMatrix shaderInvLocalMatrix; - SkAssertResult(paint.getShader()->getLocalMatrix().invert(&shaderInvLocalMatrix)); - - const auto sample = SkPoint::Make(0.5f, 0.5f); - const auto mappedSample = shaderInvLocalMatrix.mapXY(sample.x(), sample.y()), - mappedPoint = shaderInvLocalMatrix.mapXY(p0.x(), p0.y()); - const auto localMatrix = SkMatrix::MakeTrans(mappedSample.x() - mappedPoint.x(), - mappedSample.y() - mappedPoint.y()); - - SkShader::ContextRec rec(paint, SkMatrix::I(), &localMatrix, - SkShader::ContextRec::kPMColor_DstType, dstColorSpace); - auto* ctx = paint.getShader()->makeContext(rec, alloc); - if (!ctx) { - return nullptr; - } - - SkPMColor pmColor; - ctx->shadeSpan(SkScalarFloorToInt(sample.x()), SkScalarFloorToInt(sample.y()), &pmColor, 1); - - // no need to keep this temp context around. - alloc->reset(); - - return alloc->makeSkSp(SkUnPreMultiply::PMColorToColor(pmColor)); -} - -} // anonymous ns - -void SkDraw::drawVertices(SkVertices::VertexMode vmode, int count, - const SkPoint vertices[], const SkPoint textures[], - const SkColor colors[], SkBlendMode bmode, - const uint16_t indices[], int indexCount, - const SkPaint& paint) const { - SkASSERT(0 == count || vertices); - - // abort early if there is nothing to draw - if (count < 3 || (indices && indexCount < 3) || fRC->isEmpty()) { - return; - } - - // transform out vertices into device coordinates - SkAutoSTMalloc<16, SkPoint> storage(count); - SkPoint* devVerts = storage.get(); - fMatrix->mapPoints(devVerts, vertices, count); - - /* - We can draw the vertices in 1 of 4 ways: - - - solid color (no shader/texture[], no colors[]) - - just colors (no shader/texture[], has colors[]) - - just texture (has shader/texture[], no colors[]) - - colors * texture (has shader/texture[], has colors[]) - - Thus for texture drawing, we need both texture[] and a shader. - */ - - auto triShader = sk_make_sp(); - SkPaint p(paint); - - SkShader* shader = p.getShader(); - if (nullptr == shader) { - // if we have no shader, we ignore the texture coordinates - textures = nullptr; - } else if (nullptr == textures) { - // if we don't have texture coordinates, ignore the shader - p.setShader(nullptr); - shader = nullptr; - } - - // setup the custom shader (if needed) - if (colors) { - if (nullptr == textures) { - // just colors (no texture) - p.setShader(triShader); - } else { - // colors * texture - SkASSERT(shader); - p.setShader(SkShader::MakeComposeShader(triShader, sk_ref_sp(shader), bmode)); - } - } - - SkAutoBlitterChoose blitter(fDst, *fMatrix, p); - // Abort early if we failed to create a shader context. - if (blitter->isNullBlitter()) { - return; - } - - // setup our state and function pointer for iterating triangles - VertState state(count, indices, indexCount); - VertState::Proc vertProc = state.chooseProc(vmode); - - if (textures || colors) { - SkTriColorShader::TriColorShaderData verticesSetup = { vertices, colors, &state }; - - while (vertProc(&state)) { - auto* blitterPtr = blitter.get(); - - // We're going to allocate at most - // - // * one SkLocalMatrixShader OR one SkColorShader - // * one SkComposeShader - // * one SkAutoBlitterChoose - // - static constexpr size_t kAllocSize = - sizeof(SkAutoBlitterChoose) + sizeof(SkComposeShader) + - SkTMax(sizeof(SkLocalInnerMatrixShader), sizeof(SkColorShader)); - char allocBuffer[kAllocSize]; - SkArenaAlloc alloc(allocBuffer); - - if (textures) { - sk_sp texShader = MakeTextureShader(state, vertices, textures, paint, - fDst.colorSpace(), &alloc); - if (texShader) { - SkPaint localPaint(p); - localPaint.setShader(colors - ? alloc.makeSkSp(triShader, std::move(texShader), bmode) - : std::move(texShader)); - - blitterPtr = alloc.make(fDst, *fMatrix, localPaint)->get(); - if (blitterPtr->isNullBlitter()) { - continue; - } - } - } - if (colors) { - triShader->bindSetupData(&verticesSetup); - } - - SkPoint tmp[] = { - devVerts[state.f0], devVerts[state.f1], devVerts[state.f2] - }; - SkScan::FillTriangle(tmp, *fRC, blitterPtr); - triShader->bindSetupData(nullptr); - } - } else { - // no colors[] and no texture, stroke hairlines with paint's color. - SkScan::HairRCProc hairProc = ChooseHairProc(paint.isAntiAlias()); - const SkRasterClip& clip = *fRC; - while (vertProc(&state)) { - SkPoint array[] = { - devVerts[state.f0], devVerts[state.f1], devVerts[state.f2], devVerts[state.f0] - }; - hairProc(array, 4, clip, blitter.get()); - } - } -} - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////// #ifdef SK_DEBUG diff --git a/src/core/SkDraw_vertices.cpp b/src/core/SkDraw_vertices.cpp new file mode 100644 index 0000000000..dcf8d585ea --- /dev/null +++ b/src/core/SkDraw_vertices.cpp @@ -0,0 +1,479 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkArenaAlloc.h" +#include "SkAutoBlitterChoose.h" +#include "SkColorShader.h" +#include "SkDraw.h" +#include "SkNx.h" +#include "SkPM4f.h" +#include "SkRasterClip.h" +#include "SkScan.h" +#include "SkShader.h" +#include "SkString.h" +#include "SkVertState.h" + +struct Matrix43 { + float fMat[12]; // column major + + Sk4f map(float x, float y) const { + return Sk4f::Load(&fMat[0]) * x + Sk4f::Load(&fMat[4]) * y + Sk4f::Load(&fMat[8]); + } + + void setConcat(const Matrix43& a, const SkMatrix& b) { + fMat[ 0] = a.dot(0, b.getScaleX(), b.getSkewY()); + fMat[ 1] = a.dot(1, b.getScaleX(), b.getSkewY()); + fMat[ 2] = a.dot(2, b.getScaleX(), b.getSkewY()); + fMat[ 3] = a.dot(3, b.getScaleX(), b.getSkewY()); + + fMat[ 4] = a.dot(0, b.getSkewX(), b.getScaleY()); + fMat[ 5] = a.dot(1, b.getSkewX(), b.getScaleY()); + fMat[ 6] = a.dot(2, b.getSkewX(), b.getScaleY()); + fMat[ 7] = a.dot(3, b.getSkewX(), b.getScaleY()); + + fMat[ 8] = a.dot(0, b.getTranslateX(), b.getTranslateY()) + a.fMat[ 8]; + fMat[ 9] = a.dot(1, b.getTranslateX(), b.getTranslateY()) + a.fMat[ 9]; + fMat[10] = a.dot(2, b.getTranslateX(), b.getTranslateY()) + a.fMat[10]; + fMat[11] = a.dot(3, b.getTranslateX(), b.getTranslateY()) + a.fMat[11]; + } + +private: + float dot(int index, float x, float y) const { + return fMat[index + 0] * x + fMat[index + 4] * y; + } +}; + +static SkScan::HairRCProc ChooseHairProc(bool doAntiAlias) { + return doAntiAlias ? SkScan::AntiHairLine : SkScan::HairLine; +} + +static bool texture_to_matrix(const VertState& state, const SkPoint verts[], + const SkPoint texs[], SkMatrix* matrix) { + SkPoint src[3], dst[3]; + + src[0] = texs[state.f0]; + src[1] = texs[state.f1]; + src[2] = texs[state.f2]; + dst[0] = verts[state.f0]; + dst[1] = verts[state.f1]; + dst[2] = verts[state.f2]; + return matrix->setPolyToPoly(src, dst, 3); +} + +class SkTriColorShader : public SkShader { +public: + SkTriColorShader(); + + class TriColorShaderContext : public SkShader::Context { + public: + TriColorShaderContext(const SkTriColorShader& shader, const ContextRec&); + ~TriColorShaderContext() override; + void shadeSpan(int x, int y, SkPMColor dstC[], int count) override; + void shadeSpan4f(int x, int y, SkPM4f dstC[], int count) override; + + private: + bool setup(const SkPoint pts[], const SkColor colors[], int, int, int); + + SkMatrix fDstToUnit; + SkPMColor fColors[3]; + bool fSetup; + + Matrix43 fM43; + + typedef SkShader::Context INHERITED; + }; + + struct TriColorShaderData { + const SkPoint* pts; + const SkColor* colors; + const VertState *state; + }; + + SK_TO_STRING_OVERRIDE() + + // For serialization. This will never be called. + Factory getFactory() const override { sk_throw(); return nullptr; } + + // Supply setup data to context from drawing setup + void bindSetupData(TriColorShaderData* setupData) { fSetupData = setupData; } + + // Take the setup data from context when needed. + TriColorShaderData* takeSetupData() { + TriColorShaderData *data = fSetupData; + fSetupData = NULL; + return data; + } + +protected: + Context* onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc) const override { + return alloc->make(*this, rec); + } + +private: + TriColorShaderData *fSetupData; + + typedef SkShader INHERITED; +}; + +bool SkTriColorShader::TriColorShaderContext::setup(const SkPoint pts[], const SkColor colors[], + int index0, int index1, int index2) { + + fColors[0] = SkPreMultiplyColor(colors[index0]); + fColors[1] = SkPreMultiplyColor(colors[index1]); + fColors[2] = SkPreMultiplyColor(colors[index2]); + + SkMatrix m, im; + m.reset(); + m.set(0, pts[index1].fX - pts[index0].fX); + m.set(1, pts[index2].fX - pts[index0].fX); + m.set(2, pts[index0].fX); + m.set(3, pts[index1].fY - pts[index0].fY); + m.set(4, pts[index2].fY - pts[index0].fY); + m.set(5, pts[index0].fY); + if (!m.invert(&im)) { + return false; + } + // We can't call getTotalInverse(), because we explicitly don't want to look at the localmatrix + // as our interators are intrinsically tied to the vertices, and nothing else. + SkMatrix ctmInv; + if (!this->getCTM().invert(&ctmInv)) { + return false; + } + // TODO replace INV(m) * INV(ctm) with INV(ctm * m) + fDstToUnit.setConcat(im, ctmInv); + + Sk4f alpha(this->getPaintAlpha() * (1.0f / 255)), + c0 = SkPM4f::FromPMColor(fColors[0]).to4f() * alpha, + c1 = SkPM4f::FromPMColor(fColors[1]).to4f() * alpha, + c2 = SkPM4f::FromPMColor(fColors[2]).to4f() * alpha; + + Matrix43 colorm; + (c1 - c0).store(&colorm.fMat[0]); + (c2 - c0).store(&colorm.fMat[4]); + c0.store(&colorm.fMat[8]); + fM43.setConcat(colorm, fDstToUnit); + + return true; +} + +#include "SkColorPriv.h" +#include "SkComposeShader.h" + +static int ScalarTo256(SkScalar v) { + return static_cast(SkScalarPin(v, 0, 1) * 256 + 0.5); +} + +SkTriColorShader::SkTriColorShader() +: INHERITED(NULL) +, fSetupData(NULL) {} + +SkTriColorShader::TriColorShaderContext::TriColorShaderContext(const SkTriColorShader& shader, + const ContextRec& rec) +: INHERITED(shader, rec) +, fSetup(false) {} + +SkTriColorShader::TriColorShaderContext::~TriColorShaderContext() {} + +void SkTriColorShader::TriColorShaderContext::shadeSpan(int x, int y, SkPMColor dstC[], int count) { + SkTriColorShader* parent = static_cast(const_cast(&fShader)); + TriColorShaderData* set = parent->takeSetupData(); + if (set) { + fSetup = setup(set->pts, set->colors, set->state->f0, set->state->f1, set->state->f2); + } + + if (!fSetup) { + // Invalid matrices. Not checked before so no need to assert. + return; + } + + const int alphaScale = Sk255To256(this->getPaintAlpha()); + + SkPoint src; + + fDstToUnit.mapXY(SkIntToScalar(x) + 0.5, SkIntToScalar(y) + 0.5, &src); + for (int i = 0; i < count; i++) { + int scale1 = ScalarTo256(src.fX); + int scale2 = ScalarTo256(src.fY); + int scale0 = 256 - scale1 - scale2; + if (scale0 < 0) { + if (scale1 > scale2) { + scale2 = 256 - scale1; + } else { + scale1 = 256 - scale2; + } + scale0 = 0; + } + + if (256 != alphaScale) { + scale0 = SkAlphaMul(scale0, alphaScale); + scale1 = SkAlphaMul(scale1, alphaScale); + scale2 = SkAlphaMul(scale2, alphaScale); + } + + dstC[i] = SkAlphaMulQ(fColors[0], scale0) + + SkAlphaMulQ(fColors[1], scale1) + + SkAlphaMulQ(fColors[2], scale2); + + src.fX += fDstToUnit.getScaleX(); + src.fY += fDstToUnit.getSkewY(); + } +} + +void SkTriColorShader::TriColorShaderContext::shadeSpan4f(int x, int y, SkPM4f dstC[], int count) { + SkTriColorShader* parent = static_cast(const_cast(&fShader)); + TriColorShaderData* set = parent->takeSetupData(); + if (set) { + fSetup = setup(set->pts, set->colors, set->state->f0, set->state->f1, set->state->f2); + } + + if (!fSetup) { + // Invalid matrices. Not checked before so no need to assert. + return; + } + + Sk4f c = fM43.map(SkIntToScalar(x) + 0.5, SkIntToScalar(y) + 0.5), + dc = Sk4f::Load(&fM43.fMat[0]), + zero(0.0f), + one(1.0f); + + for (int i = 0; i < count; i++) { + // We don't expect to be wildly out of 0...1, but we pin just because of minor + // numerical imprecision. + Sk4f::Min(Sk4f::Max(c, zero), Sk4f::Min(c[3], one)).store(dstC + i); + c += dc; + } +} + +#ifndef SK_IGNORE_TO_STRING +void SkTriColorShader::toString(SkString* str) const { + str->append("SkTriColorShader: ("); + + this->INHERITED::toString(str); + + str->append(")"); +} +#endif + + +namespace { + + // Similar to SkLocalMatrixShader, but composes the local matrix with the CTM (instead + // of composing with the inherited local matrix): + // + // rec' = {rec.ctm x localMatrix, rec.localMatrix} + // + // (as opposed to rec' = {rec.ctm, rec.localMatrix x localMatrix}) + // + class SkLocalInnerMatrixShader final : public SkShader { + public: + SkLocalInnerMatrixShader(sk_sp proxy, const SkMatrix& localMatrix) + : INHERITED(&localMatrix) + , fProxyShader(std::move(proxy)) {} + + Factory getFactory() const override { + SkASSERT(false); + return nullptr; + } + + protected: + void flatten(SkWriteBuffer&) const override { + SkASSERT(false); + } + + Context* onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc) const override { + SkMatrix adjustedCTM = SkMatrix::Concat(*rec.fMatrix, this->getLocalMatrix()); + ContextRec newRec(rec); + newRec.fMatrix = &adjustedCTM; + return fProxyShader->makeContext(newRec, alloc); + } + + bool onAppendStages(SkRasterPipeline* p, SkColorSpace* cs, SkArenaAlloc* alloc, + const SkMatrix& ctm, const SkPaint& paint, + const SkMatrix* localM) const override { + // We control the shader graph ancestors, so we know there's no local matrix being + // injected before this. + SkASSERT(!localM); + + SkMatrix adjustedCTM = SkMatrix::Concat(ctm, this->getLocalMatrix()); + return fProxyShader->appendStages(p, cs, alloc, adjustedCTM, paint); + } + + private: + sk_sp fProxyShader; + + typedef SkShader INHERITED; + }; + + sk_sp MakeTextureShader(const VertState& state, const SkPoint verts[], + const SkPoint texs[], const SkPaint& paint, + SkColorSpace* dstColorSpace, + SkArenaAlloc* alloc) { + SkASSERT(paint.getShader()); + + const auto& p0 = texs[state.f0], + p1 = texs[state.f1], + p2 = texs[state.f2]; + + if (p0 != p1 || p0 != p2) { + // Common case (non-collapsed texture coordinates). + // Map the texture to vertices using a local transform. + + // We cannot use a plain SkLocalMatrix shader, because we need the texture matrix + // to compose next to the CTM. + SkMatrix localMatrix; + return texture_to_matrix(state, verts, texs, &localMatrix) + ? alloc->makeSkSp(paint.refShader(), localMatrix) + : nullptr; + } + + // Collapsed texture coordinates special case. + // The texture is a solid color, sampled at the given point. + SkMatrix shaderInvLocalMatrix; + SkAssertResult(paint.getShader()->getLocalMatrix().invert(&shaderInvLocalMatrix)); + + const auto sample = SkPoint::Make(0.5f, 0.5f); + const auto mappedSample = shaderInvLocalMatrix.mapXY(sample.x(), sample.y()), + mappedPoint = shaderInvLocalMatrix.mapXY(p0.x(), p0.y()); + const auto localMatrix = SkMatrix::MakeTrans(mappedSample.x() - mappedPoint.x(), + mappedSample.y() - mappedPoint.y()); + + SkShader::ContextRec rec(paint, SkMatrix::I(), &localMatrix, + SkShader::ContextRec::kPMColor_DstType, dstColorSpace); + auto* ctx = paint.getShader()->makeContext(rec, alloc); + if (!ctx) { + return nullptr; + } + + SkPMColor pmColor; + ctx->shadeSpan(SkScalarFloorToInt(sample.x()), SkScalarFloorToInt(sample.y()), &pmColor, 1); + + // no need to keep this temp context around. + alloc->reset(); + + return alloc->makeSkSp(SkUnPreMultiply::PMColorToColor(pmColor)); + } + +} // anonymous ns + +void SkDraw::drawVertices(SkVertices::VertexMode vmode, int count, + const SkPoint vertices[], const SkPoint textures[], + const SkColor colors[], SkBlendMode bmode, + const uint16_t indices[], int indexCount, + const SkPaint& paint) const { + SkASSERT(0 == count || vertices); + + // abort early if there is nothing to draw + if (count < 3 || (indices && indexCount < 3) || fRC->isEmpty()) { + return; + } + + // transform out vertices into device coordinates + SkAutoSTMalloc<16, SkPoint> storage(count); + SkPoint* devVerts = storage.get(); + fMatrix->mapPoints(devVerts, vertices, count); + + /* + We can draw the vertices in 1 of 4 ways: + + - solid color (no shader/texture[], no colors[]) + - just colors (no shader/texture[], has colors[]) + - just texture (has shader/texture[], no colors[]) + - colors * texture (has shader/texture[], has colors[]) + + Thus for texture drawing, we need both texture[] and a shader. + */ + + auto triShader = sk_make_sp(); + SkPaint p(paint); + + SkShader* shader = p.getShader(); + if (nullptr == shader) { + // if we have no shader, we ignore the texture coordinates + textures = nullptr; + } else if (nullptr == textures) { + // if we don't have texture coordinates, ignore the shader + p.setShader(nullptr); + shader = nullptr; + } + + // setup the custom shader (if needed) + if (colors) { + if (nullptr == textures) { + // just colors (no texture) + p.setShader(triShader); + } else { + // colors * texture + SkASSERT(shader); + p.setShader(SkShader::MakeComposeShader(triShader, sk_ref_sp(shader), bmode)); + } + } + + SkAutoBlitterChoose blitter(fDst, *fMatrix, p); + // Abort early if we failed to create a shader context. + if (blitter->isNullBlitter()) { + return; + } + + // setup our state and function pointer for iterating triangles + VertState state(count, indices, indexCount); + VertState::Proc vertProc = state.chooseProc(vmode); + + if (textures || colors) { + SkTriColorShader::TriColorShaderData verticesSetup = { vertices, colors, &state }; + + while (vertProc(&state)) { + auto* blitterPtr = blitter.get(); + + // We're going to allocate at most + // + // * one SkLocalMatrixShader OR one SkColorShader + // * one SkComposeShader + // * one SkAutoBlitterChoose + // + static constexpr size_t kAllocSize = + sizeof(SkAutoBlitterChoose) + sizeof(SkComposeShader) + + SkTMax(sizeof(SkLocalInnerMatrixShader), sizeof(SkColorShader)); + char allocBuffer[kAllocSize]; + SkArenaAlloc alloc(allocBuffer); + + if (textures) { + sk_sp texShader = MakeTextureShader(state, vertices, textures, paint, + fDst.colorSpace(), &alloc); + if (texShader) { + SkPaint localPaint(p); + localPaint.setShader(colors + ? alloc.makeSkSp(triShader, std::move(texShader), bmode) + : std::move(texShader)); + + blitterPtr = alloc.make(fDst, *fMatrix, localPaint)->get(); + if (blitterPtr->isNullBlitter()) { + continue; + } + } + } + if (colors) { + triShader->bindSetupData(&verticesSetup); + } + + SkPoint tmp[] = { + devVerts[state.f0], devVerts[state.f1], devVerts[state.f2] + }; + SkScan::FillTriangle(tmp, *fRC, blitterPtr); + triShader->bindSetupData(nullptr); + } + } else { + // no colors[] and no texture, stroke hairlines with paint's color. + SkScan::HairRCProc hairProc = ChooseHairProc(paint.isAntiAlias()); + const SkRasterClip& clip = *fRC; + while (vertProc(&state)) { + SkPoint array[] = { + devVerts[state.f0], devVerts[state.f1], devVerts[state.f2], devVerts[state.f0] + }; + hairProc(array, 4, clip, blitter.get()); + } + } +}