Use rasterpipeline for drawVertices
Bug: skia:
Change-Id: If6da119ee98f26981cef9373162ddb526db77be5
Reviewed-on: https://skia-review.googlesource.com/17422
Commit-Queue: Mike Reed <reed@google.com>
Reviewed-by: Florin Malita <fmalita@chromium.org>
Reviewed-by: Mike Klein <mtklein@google.com>
diff --git a/src/core/SkDraw_vertices.cpp b/src/core/SkDraw_vertices.cpp
index 398d02c..442ba08 100644
--- a/src/core/SkDraw_vertices.cpp
+++ b/src/core/SkDraw_vertices.cpp
@@ -7,7 +7,7 @@
#include "SkArenaAlloc.h"
#include "SkAutoBlitterChoose.h"
-#include "SkColorShader.h"
+#include "SkComposeShader.h"
#include "SkDraw.h"
#include "SkNx.h"
#include "SkPM4fPriv.h"
@@ -17,7 +17,6 @@
#include "SkString.h"
#include "SkVertState.h"
-#include "SkRasterPipeline.h"
#include "SkArenaAlloc.h"
#include "SkCoreBlitters.h"
#include "SkColorSpaceXform.h"
@@ -72,188 +71,40 @@
class SkTriColorShader : public SkShader {
public:
- SkTriColorShader();
+ SkTriColorShader(bool isOpaque) : fIsOpaque(isOpaque) {}
- 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;
+ Matrix43* getMatrix43() { return &fM43; }
- 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;
- };
+ bool isOpaque() const override { return fIsOpaque; }
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<TriColorShaderContext>(*this, rec);
+ return nullptr;
+ }
+ bool onAppendStages(SkRasterPipeline* pipeine, SkColorSpace* dstCS, SkArenaAlloc* alloc,
+ const SkMatrix&, const SkPaint&, const SkMatrix*) const override {
+ pipeine->append(SkRasterPipeline::matrix_4x3, &fM43);
+ // In theory we should never need to clamp. However, either due to imprecision in our
+ // matrix43, or the scan converter passing us pixel centers that in fact are not within
+ // the triangle, we do see occasional (slightly) out-of-range values, so we add these
+ // clamp stages. It would be nice to find a way to detect when these are not needed.
+ pipeine->append(SkRasterPipeline::clamp_0);
+ pipeine->append(SkRasterPipeline::clamp_a);
+ return true;
}
private:
- TriColorShaderData *fSetupData;
+ Matrix43 fM43;
+ const bool fIsOpaque;
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<int>(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<SkTriColorShader*>(const_cast<SkShader*>(&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<SkTriColorShader*>(const_cast<SkShader*>(&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: (");
@@ -264,106 +115,6 @@
}
#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<SkShader> 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<SkShader> fProxyShader;
-
- typedef SkShader INHERITED;
- };
-
- sk_sp<SkShader> 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<SkLocalInnerMatrixShader>(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<SkColorShader>(SkUnPreMultiply::PMColorToColor(pmColor));
- }
-} // anonymous ns
-
static bool update_tricolor_matrix(const SkMatrix& ctmInv,
const SkPoint pts[], const SkPM4f colors[],
int index0, int index1, int index2, Matrix43* result) {
@@ -394,6 +145,15 @@
return true;
}
+// Convert the SkColors into float colors. The conversion depends on some conditions:
+// - If the pixmap has a dst colorspace, we have to be "color-correct".
+// Do we map into dst-colorspace before or after we interpolate?
+// - We have to decide when to apply per-color alpha (before or after we interpolate)
+//
+// For now, we will take a simple approach, but recognize this is just a start:
+// - convert colors into dst colorspace before interpolation (matches gradients)
+// - apply per-color alpha before interpolation (matches old version of vertices)
+//
static SkPM4f* convert_colors(const SkColor src[], int count, SkColorSpace* deviceCS,
SkArenaAlloc* alloc) {
SkPM4f* dst = alloc->makeArray<SkPM4f>(count);
@@ -435,159 +195,81 @@
return;
}
- // transform out vertices into device coordinates
- SkAutoSTMalloc<16, SkPoint> storage(count);
- SkPoint* devVerts = storage.get();
+ // make textures and shader mutually consistent
+ SkShader* shader = paint.getShader();
+ if (!(shader && textures)) {
+ shader = nullptr;
+ textures = nullptr;
+ }
+
+ constexpr size_t defCount = 16;
+ constexpr size_t outerSize = sizeof(SkTriColorShader) +
+ sizeof(SkComposeShader) +
+ (sizeof(SkPoint) + sizeof(SkPM4f)) * defCount;
+ char outerStorage[outerSize];
+ SkArenaAlloc outerAlloc(outerStorage, sizeof(outerStorage));
+
+ SkPoint* devVerts = outerAlloc.makeArray<SkPoint>(count);
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.
- */
-
- if (colors && !textures) {
- char arenaStorage[4096];
- SkArenaAlloc alloc(arenaStorage, sizeof(storage));
- Matrix43 matrix43;
- SkRasterPipeline shaderPipeline(&alloc);
-
- // Convert the SkColors into float colors. The conversion depends on some conditions:
- // - If the pixmap has a dst colorspace, we have to be "color-correct".
- // Do we map into dst-colorspace before or after we interpolate?
- // - We have to decide when to apply per-color alpha (before or after we interpolate)
- //
- // For now, we will take a simple approach, but recognize this is just a start:
- // - convert colors into dst colorspace before interpolation (matches gradients)
- // - apply per-color alpha before interpolation (matches old version of vertices)
- //
- SkPM4f* dstColors = convert_colors(colors, count, fDst.colorSpace(), &alloc);
-
- bool is_opaque;
- if (paint.getAlpha() == 0xff) {
- is_opaque = compute_is_opaque(colors, count);
- } else {
- is_opaque = false;
- Sk4f alpha = paint.getAlpha() * (1/255.0f);
- for (int i = 0; i < count; i++) {
- (dstColors[i].to4f() * alpha).store(dstColors + i);
- }
- }
-
- shaderPipeline.append(SkRasterPipeline::matrix_4x3, &matrix43);
- // In theory we should never need to clamp. However, either due to imprecision in our
- // matrix43, or the scan converter passing us pixel centers that in fact are not within
- // the triangle, we do see occasional (slightly) out-of-range values, so we add these
- // clamp stages. It would be nice to find a way to detect when these are not needed.
- shaderPipeline.append(SkRasterPipeline::clamp_0);
- shaderPipeline.append(SkRasterPipeline::clamp_a);
-
- bool wants_dither = paint.isDither();
- auto blitter = SkCreateRasterPipelineBlitter(fDst, paint, shaderPipeline,
- is_opaque, wants_dither, &alloc);
- SkASSERT(!blitter->isNullBlitter());
-
- // setup our state and function pointer for iterating triangles
- VertState state(count, indices, indexCount);
- VertState::Proc vertProc = state.chooseProc(vmode);
-
- while (vertProc(&state)) {
- SkPoint tmp[] = {
- devVerts[state.f0], devVerts[state.f1], devVerts[state.f2]
- };
- if (update_tricolor_matrix(ctmInv, vertices, dstColors, state.f0, state.f1, state.f2,
- &matrix43)) {
- SkScan::FillTriangle(tmp, *fRC, blitter);
- }
- }
- return;
- }
-
- auto triShader = sk_make_sp<SkTriColorShader>();
- 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 };
+ if (colors || textures) {
+ SkPM4f* dstColors = nullptr;
+ Matrix43* matrix43 = nullptr;
+
+ if (colors) {
+ dstColors = convert_colors(colors, count, fDst.colorSpace(), &outerAlloc);
+
+ SkTriColorShader* triShader = outerAlloc.make<SkTriColorShader>(
+ compute_is_opaque(colors, count));
+ matrix43 = triShader->getMatrix43();
+ if (shader) {
+ shader = outerAlloc.make<SkComposeShader>(sk_ref_sp(triShader), sk_ref_sp(shader),
+ bmode);
+ } else {
+ shader = triShader;
+ }
+ }
+
+ SkPaint p(paint);
+ p.setShader(sk_ref_sp(shader));
while (vertProc(&state)) {
- auto* blitterPtr = blitter.get();
+ char innerStorage[2048];
+ SkArenaAlloc innerAlloc(innerStorage, sizeof(innerStorage));
- // 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);
-
+ const SkMatrix* ctm = fMatrix;
+ SkMatrix tmpCtm;
if (textures) {
- sk_sp<SkShader> texShader = MakeTextureShader(state, vertices, textures, paint,
- fDst.colorSpace(), &alloc);
- if (texShader) {
- SkPaint localPaint(p);
- localPaint.setShader(colors
- ? alloc.makeSkSp<SkComposeShader>(triShader, std::move(texShader), bmode)
- : std::move(texShader));
-
- blitterPtr = alloc.make<SkAutoBlitterChoose>(fDst, *fMatrix, localPaint)->get();
- if (blitterPtr->isNullBlitter()) {
- continue;
- }
- }
+ SkMatrix localM;
+ texture_to_matrix(state, vertices, textures, &localM);
+ tmpCtm = SkMatrix::Concat(*fMatrix, localM);
+ ctm = &tmpCtm;
}
- if (colors) {
- triShader->bindSetupData(&verticesSetup);
+
+ if (matrix43 && !update_tricolor_matrix(ctmInv, vertices, dstColors,
+ state.f0, state.f1, state.f2,
+ matrix43)) {
+ continue;
}
SkPoint tmp[] = {
devVerts[state.f0], devVerts[state.f1], devVerts[state.f2]
};
- SkScan::FillTriangle(tmp, *fRC, blitterPtr);
- triShader->bindSetupData(nullptr);
+ auto blitter = SkCreateRasterPipelineBlitter(fDst, p, *ctm, &innerAlloc);
+ SkScan::FillTriangle(tmp, *fRC, blitter);
}
} else {
// no colors[] and no texture, stroke hairlines with paint's color.
+ SkPaint p;
+ p.setStyle(SkPaint::kStroke_Style);
+ SkAutoBlitterChoose blitter(fDst, *fMatrix, p);
+ // Abort early if we failed to create a shader context.
+ if (blitter->isNullBlitter()) {
+ return;
+ }
SkScan::HairRCProc hairProc = ChooseHairProc(paint.isAntiAlias());
const SkRasterClip& clip = *fRC;
while (vertProc(&state)) {