/* * 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 "SkComposeShader.h" #include "SkDraw.h" #include "SkNx.h" #include "SkPM4fPriv.h" #include "SkRasterClip.h" #include "SkScan.h" #include "SkShaderBase.h" #include "SkString.h" #include "SkVertState.h" #include "SkArenaAlloc.h" #include "SkCoreBlitters.h" #include "SkColorSpaceXform.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 SK_WARN_UNUSED_RESULT 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 SkShaderBase { public: SkTriColorShader(bool isOpaque) : fIsOpaque(isOpaque) {} Matrix43* getMatrix43() { return &fM43; } bool isOpaque() const override { return fIsOpaque; } // For serialization. This will never be called. Factory getFactory() const override { SK_ABORT("not reached"); return nullptr; } protected: Context* onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc) const override { return nullptr; } bool onAppendStages(const StageRec& rec) const override { rec.fPipeline->append(SkRasterPipeline::seed_shader); rec.fPipeline->append(SkRasterPipeline::matrix_4x3, &fM43); return true; } private: Matrix43 fM43; const bool fIsOpaque; typedef SkShaderBase INHERITED; }; static bool SK_WARN_UNUSED_RESULT update_tricolor_matrix(const SkMatrix& ctmInv, const SkPoint pts[], const SkPM4f colors[], int index0, int index1, int index2, Matrix43* result) { 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; } SkMatrix dstToUnit; dstToUnit.setConcat(im, ctmInv); Sk4f c0 = colors[index0].to4f(), c1 = colors[index1].to4f(), c2 = colors[index2].to4f(); Matrix43 colorm; (c1 - c0).store(&colorm.fMat[0]); (c2 - c0).store(&colorm.fMat[4]); c0.store(&colorm.fMat[8]); result->setConcat(colorm, dstToUnit); 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(count); if (!deviceCS) { for (int i = 0; i < count; ++i) { SkColor4f color4f; swizzle_rb(Sk4f_fromL32(src[i])).store(&color4f); dst[i] = color4f.premul(); } } else { auto srcCS = SkColorSpace::MakeSRGB(); SkColorSpaceXform::Apply(deviceCS , SkColorSpaceXform::kRGBA_F32_ColorFormat, dst, srcCS.get(), SkColorSpaceXform::kBGRA_8888_ColorFormat, src, count, SkColorSpaceXform::kPremul_AlphaOp); } return dst; } static bool compute_is_opaque(const SkColor colors[], int count) { uint32_t c = ~0; for (int i = 0; i < count; ++i) { c &= colors[i]; } return SkColorGetA(c) == 0xFF; } void SkDraw::drawVertices(SkVertices::VertexMode vmode, int vertexCount, const SkPoint vertices[], const SkPoint textures[], const SkColor colors[], const SkVertices::BoneIndices boneIndices[], const SkVertices::BoneWeights boneWeights[], SkBlendMode bmode, const uint16_t indices[], int indexCount, const SkPaint& paint, const SkMatrix* bones, int boneCount) const { SkASSERT(0 == vertexCount || vertices); // abort early if there is nothing to draw if (vertexCount < 3 || (indices && indexCount < 3) || fRC->isEmpty()) { return; } SkMatrix ctmInv; if (!fMatrix->invert(&ctmInv)) { return; } // make textures and shader mutually consistent SkShader* shader = paint.getShader(); if (!(shader && textures)) { shader = nullptr; textures = nullptr; } // We can simplify things for certain blendmodes. This is for speed, and SkComposeShader // itself insists we don't pass kSrc or kDst to it. // if (colors && textures) { switch (bmode) { case SkBlendMode::kSrc: colors = nullptr; break; case SkBlendMode::kDst: textures = nullptr; break; default: break; } } // we don't use the shader if there are no textures if (!textures) { shader = nullptr; } constexpr size_t kDefVertexCount = 16; constexpr size_t kDefBoneCount = 8; constexpr size_t kOuterSize = sizeof(SkTriColorShader) + sizeof(SkComposeShader) + (2 * sizeof(SkPoint) + sizeof(SkPM4f)) * kDefVertexCount + sizeof(SkMatrix) * kDefBoneCount; SkSTArenaAlloc outerAlloc; // deform vertices using the skeleton if it is passed in if (bones && boneCount) { // allocate space for the deformed vertices SkPoint* deformed = outerAlloc.makeArray(vertexCount); // get the bone matrices SkMatrix* transformedBones = outerAlloc.makeArray(boneCount); // transform the bone matrices by the world transform transformedBones[0] = bones[0]; for (int i = 1; i < boneCount; i ++) { transformedBones[i] = SkMatrix::Concat(bones[i], bones[0]); } // deform the vertices if (boneIndices && boneWeights) { for (int i = 0; i < vertexCount; i ++) { const SkVertices::BoneIndices& indices = boneIndices[i]; const SkVertices::BoneWeights& weights = boneWeights[i]; // apply bone deformations SkPoint result = SkPoint::Make(0.0f, 0.0f); SkPoint transformed; for (uint32_t j = 0; j < 4; j ++) { // get the attachment data uint32_t index = indices.indices[j]; float weight = weights.weights[j]; // skip the bone if there is no weight if (weight == 0.0f) { continue; } SkASSERT(index != 0); // transformed = M * v transformedBones[index].mapPoints(&transformed, &vertices[i], 1); // result += transformed * w result += transformed * weight; } // set the deformed point deformed[i] = result; } } else { // no bones, so only apply world transform const SkMatrix& worldTransform = bones[0]; worldTransform.mapPoints(deformed, vertices, vertexCount); } // change the vertices to point to deformed vertices = deformed; } SkPoint* devVerts = outerAlloc.makeArray(vertexCount); fMatrix->mapPoints(devVerts, vertices, vertexCount); { SkRect bounds; // this also sets bounds to empty if we see a non-finite value bounds.set(devVerts, vertexCount); if (bounds.isEmpty()) { return; } } VertState state(vertexCount, indices, indexCount); VertState::Proc vertProc = state.chooseProc(vmode); if (colors || textures) { SkPM4f* dstColors = nullptr; Matrix43* matrix43 = nullptr; if (colors) { dstColors = convert_colors(colors, vertexCount, fDst.colorSpace(), &outerAlloc); SkTriColorShader* triShader = outerAlloc.make( compute_is_opaque(colors, vertexCount)); matrix43 = triShader->getMatrix43(); if (shader) { shader = outerAlloc.make(sk_ref_sp(triShader), sk_ref_sp(shader), bmode, 1); } else { shader = triShader; } } SkPaint p(paint); p.setShader(sk_ref_sp(shader)); if (!textures) { // only tricolor shader SkASSERT(matrix43); auto blitter = SkCreateRasterPipelineBlitter(fDst, p, *fMatrix, &outerAlloc); while (vertProc(&state)) { if (!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, blitter); } } else { while (vertProc(&state)) { SkSTArenaAlloc<2048> innerAlloc; const SkMatrix* ctm = fMatrix; SkMatrix tmpCtm; if (textures) { SkMatrix localM; if (!texture_to_matrix(state, vertices, textures, &localM)) { continue; } tmpCtm = SkMatrix::Concat(*fMatrix, localM); ctm = &tmpCtm; } 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] }; 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(*this, nullptr, 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)) { SkPoint array[] = { devVerts[state.f0], devVerts[state.f1], devVerts[state.f2], devVerts[state.f0] }; hairProc(array, 4, clip, blitter.get()); } } }