Mike Reed | 787a16d | 2017-05-15 09:29:18 -0400 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2017 Google Inc. |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license that can be |
| 5 | * found in the LICENSE file. |
| 6 | */ |
| 7 | |
| 8 | #include "SkArenaAlloc.h" |
| 9 | #include "SkAutoBlitterChoose.h" |
| 10 | #include "SkColorShader.h" |
| 11 | #include "SkDraw.h" |
| 12 | #include "SkNx.h" |
| 13 | #include "SkPM4f.h" |
| 14 | #include "SkRasterClip.h" |
| 15 | #include "SkScan.h" |
| 16 | #include "SkShader.h" |
| 17 | #include "SkString.h" |
| 18 | #include "SkVertState.h" |
| 19 | |
| 20 | struct Matrix43 { |
| 21 | float fMat[12]; // column major |
| 22 | |
| 23 | Sk4f map(float x, float y) const { |
| 24 | return Sk4f::Load(&fMat[0]) * x + Sk4f::Load(&fMat[4]) * y + Sk4f::Load(&fMat[8]); |
| 25 | } |
| 26 | |
| 27 | void setConcat(const Matrix43& a, const SkMatrix& b) { |
| 28 | fMat[ 0] = a.dot(0, b.getScaleX(), b.getSkewY()); |
| 29 | fMat[ 1] = a.dot(1, b.getScaleX(), b.getSkewY()); |
| 30 | fMat[ 2] = a.dot(2, b.getScaleX(), b.getSkewY()); |
| 31 | fMat[ 3] = a.dot(3, b.getScaleX(), b.getSkewY()); |
| 32 | |
| 33 | fMat[ 4] = a.dot(0, b.getSkewX(), b.getScaleY()); |
| 34 | fMat[ 5] = a.dot(1, b.getSkewX(), b.getScaleY()); |
| 35 | fMat[ 6] = a.dot(2, b.getSkewX(), b.getScaleY()); |
| 36 | fMat[ 7] = a.dot(3, b.getSkewX(), b.getScaleY()); |
| 37 | |
| 38 | fMat[ 8] = a.dot(0, b.getTranslateX(), b.getTranslateY()) + a.fMat[ 8]; |
| 39 | fMat[ 9] = a.dot(1, b.getTranslateX(), b.getTranslateY()) + a.fMat[ 9]; |
| 40 | fMat[10] = a.dot(2, b.getTranslateX(), b.getTranslateY()) + a.fMat[10]; |
| 41 | fMat[11] = a.dot(3, b.getTranslateX(), b.getTranslateY()) + a.fMat[11]; |
| 42 | } |
| 43 | |
| 44 | private: |
| 45 | float dot(int index, float x, float y) const { |
| 46 | return fMat[index + 0] * x + fMat[index + 4] * y; |
| 47 | } |
| 48 | }; |
| 49 | |
| 50 | static SkScan::HairRCProc ChooseHairProc(bool doAntiAlias) { |
| 51 | return doAntiAlias ? SkScan::AntiHairLine : SkScan::HairLine; |
| 52 | } |
| 53 | |
| 54 | static bool texture_to_matrix(const VertState& state, const SkPoint verts[], |
| 55 | const SkPoint texs[], SkMatrix* matrix) { |
| 56 | SkPoint src[3], dst[3]; |
| 57 | |
| 58 | src[0] = texs[state.f0]; |
| 59 | src[1] = texs[state.f1]; |
| 60 | src[2] = texs[state.f2]; |
| 61 | dst[0] = verts[state.f0]; |
| 62 | dst[1] = verts[state.f1]; |
| 63 | dst[2] = verts[state.f2]; |
| 64 | return matrix->setPolyToPoly(src, dst, 3); |
| 65 | } |
| 66 | |
| 67 | class SkTriColorShader : public SkShader { |
| 68 | public: |
| 69 | SkTriColorShader(); |
| 70 | |
| 71 | class TriColorShaderContext : public SkShader::Context { |
| 72 | public: |
| 73 | TriColorShaderContext(const SkTriColorShader& shader, const ContextRec&); |
| 74 | ~TriColorShaderContext() override; |
| 75 | void shadeSpan(int x, int y, SkPMColor dstC[], int count) override; |
| 76 | void shadeSpan4f(int x, int y, SkPM4f dstC[], int count) override; |
| 77 | |
| 78 | private: |
| 79 | bool setup(const SkPoint pts[], const SkColor colors[], int, int, int); |
| 80 | |
| 81 | SkMatrix fDstToUnit; |
| 82 | SkPMColor fColors[3]; |
| 83 | bool fSetup; |
| 84 | |
| 85 | Matrix43 fM43; |
| 86 | |
| 87 | typedef SkShader::Context INHERITED; |
| 88 | }; |
| 89 | |
| 90 | struct TriColorShaderData { |
| 91 | const SkPoint* pts; |
| 92 | const SkColor* colors; |
| 93 | const VertState *state; |
| 94 | }; |
| 95 | |
| 96 | SK_TO_STRING_OVERRIDE() |
| 97 | |
| 98 | // For serialization. This will never be called. |
| 99 | Factory getFactory() const override { sk_throw(); return nullptr; } |
| 100 | |
| 101 | // Supply setup data to context from drawing setup |
| 102 | void bindSetupData(TriColorShaderData* setupData) { fSetupData = setupData; } |
| 103 | |
| 104 | // Take the setup data from context when needed. |
| 105 | TriColorShaderData* takeSetupData() { |
| 106 | TriColorShaderData *data = fSetupData; |
| 107 | fSetupData = NULL; |
| 108 | return data; |
| 109 | } |
| 110 | |
| 111 | protected: |
| 112 | Context* onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc) const override { |
| 113 | return alloc->make<TriColorShaderContext>(*this, rec); |
| 114 | } |
| 115 | |
| 116 | private: |
| 117 | TriColorShaderData *fSetupData; |
| 118 | |
| 119 | typedef SkShader INHERITED; |
| 120 | }; |
| 121 | |
| 122 | bool SkTriColorShader::TriColorShaderContext::setup(const SkPoint pts[], const SkColor colors[], |
| 123 | int index0, int index1, int index2) { |
| 124 | |
| 125 | fColors[0] = SkPreMultiplyColor(colors[index0]); |
| 126 | fColors[1] = SkPreMultiplyColor(colors[index1]); |
| 127 | fColors[2] = SkPreMultiplyColor(colors[index2]); |
| 128 | |
| 129 | SkMatrix m, im; |
| 130 | m.reset(); |
| 131 | m.set(0, pts[index1].fX - pts[index0].fX); |
| 132 | m.set(1, pts[index2].fX - pts[index0].fX); |
| 133 | m.set(2, pts[index0].fX); |
| 134 | m.set(3, pts[index1].fY - pts[index0].fY); |
| 135 | m.set(4, pts[index2].fY - pts[index0].fY); |
| 136 | m.set(5, pts[index0].fY); |
| 137 | if (!m.invert(&im)) { |
| 138 | return false; |
| 139 | } |
| 140 | // We can't call getTotalInverse(), because we explicitly don't want to look at the localmatrix |
| 141 | // as our interators are intrinsically tied to the vertices, and nothing else. |
| 142 | SkMatrix ctmInv; |
| 143 | if (!this->getCTM().invert(&ctmInv)) { |
| 144 | return false; |
| 145 | } |
| 146 | // TODO replace INV(m) * INV(ctm) with INV(ctm * m) |
| 147 | fDstToUnit.setConcat(im, ctmInv); |
| 148 | |
| 149 | Sk4f alpha(this->getPaintAlpha() * (1.0f / 255)), |
| 150 | c0 = SkPM4f::FromPMColor(fColors[0]).to4f() * alpha, |
| 151 | c1 = SkPM4f::FromPMColor(fColors[1]).to4f() * alpha, |
| 152 | c2 = SkPM4f::FromPMColor(fColors[2]).to4f() * alpha; |
| 153 | |
| 154 | Matrix43 colorm; |
| 155 | (c1 - c0).store(&colorm.fMat[0]); |
| 156 | (c2 - c0).store(&colorm.fMat[4]); |
| 157 | c0.store(&colorm.fMat[8]); |
| 158 | fM43.setConcat(colorm, fDstToUnit); |
| 159 | |
| 160 | return true; |
| 161 | } |
| 162 | |
| 163 | #include "SkColorPriv.h" |
| 164 | #include "SkComposeShader.h" |
| 165 | |
| 166 | static int ScalarTo256(SkScalar v) { |
| 167 | return static_cast<int>(SkScalarPin(v, 0, 1) * 256 + 0.5); |
| 168 | } |
| 169 | |
| 170 | SkTriColorShader::SkTriColorShader() |
| 171 | : INHERITED(NULL) |
| 172 | , fSetupData(NULL) {} |
| 173 | |
| 174 | SkTriColorShader::TriColorShaderContext::TriColorShaderContext(const SkTriColorShader& shader, |
| 175 | const ContextRec& rec) |
| 176 | : INHERITED(shader, rec) |
| 177 | , fSetup(false) {} |
| 178 | |
| 179 | SkTriColorShader::TriColorShaderContext::~TriColorShaderContext() {} |
| 180 | |
| 181 | void SkTriColorShader::TriColorShaderContext::shadeSpan(int x, int y, SkPMColor dstC[], int count) { |
| 182 | SkTriColorShader* parent = static_cast<SkTriColorShader*>(const_cast<SkShader*>(&fShader)); |
| 183 | TriColorShaderData* set = parent->takeSetupData(); |
| 184 | if (set) { |
| 185 | fSetup = setup(set->pts, set->colors, set->state->f0, set->state->f1, set->state->f2); |
| 186 | } |
| 187 | |
| 188 | if (!fSetup) { |
| 189 | // Invalid matrices. Not checked before so no need to assert. |
| 190 | return; |
| 191 | } |
| 192 | |
| 193 | const int alphaScale = Sk255To256(this->getPaintAlpha()); |
| 194 | |
| 195 | SkPoint src; |
| 196 | |
| 197 | fDstToUnit.mapXY(SkIntToScalar(x) + 0.5, SkIntToScalar(y) + 0.5, &src); |
| 198 | for (int i = 0; i < count; i++) { |
| 199 | int scale1 = ScalarTo256(src.fX); |
| 200 | int scale2 = ScalarTo256(src.fY); |
| 201 | int scale0 = 256 - scale1 - scale2; |
| 202 | if (scale0 < 0) { |
| 203 | if (scale1 > scale2) { |
| 204 | scale2 = 256 - scale1; |
| 205 | } else { |
| 206 | scale1 = 256 - scale2; |
| 207 | } |
| 208 | scale0 = 0; |
| 209 | } |
| 210 | |
| 211 | if (256 != alphaScale) { |
| 212 | scale0 = SkAlphaMul(scale0, alphaScale); |
| 213 | scale1 = SkAlphaMul(scale1, alphaScale); |
| 214 | scale2 = SkAlphaMul(scale2, alphaScale); |
| 215 | } |
| 216 | |
| 217 | dstC[i] = SkAlphaMulQ(fColors[0], scale0) + |
| 218 | SkAlphaMulQ(fColors[1], scale1) + |
| 219 | SkAlphaMulQ(fColors[2], scale2); |
| 220 | |
| 221 | src.fX += fDstToUnit.getScaleX(); |
| 222 | src.fY += fDstToUnit.getSkewY(); |
| 223 | } |
| 224 | } |
| 225 | |
| 226 | void SkTriColorShader::TriColorShaderContext::shadeSpan4f(int x, int y, SkPM4f dstC[], int count) { |
| 227 | SkTriColorShader* parent = static_cast<SkTriColorShader*>(const_cast<SkShader*>(&fShader)); |
| 228 | TriColorShaderData* set = parent->takeSetupData(); |
| 229 | if (set) { |
| 230 | fSetup = setup(set->pts, set->colors, set->state->f0, set->state->f1, set->state->f2); |
| 231 | } |
| 232 | |
| 233 | if (!fSetup) { |
| 234 | // Invalid matrices. Not checked before so no need to assert. |
| 235 | return; |
| 236 | } |
| 237 | |
| 238 | Sk4f c = fM43.map(SkIntToScalar(x) + 0.5, SkIntToScalar(y) + 0.5), |
| 239 | dc = Sk4f::Load(&fM43.fMat[0]), |
| 240 | zero(0.0f), |
| 241 | one(1.0f); |
| 242 | |
| 243 | for (int i = 0; i < count; i++) { |
| 244 | // We don't expect to be wildly out of 0...1, but we pin just because of minor |
| 245 | // numerical imprecision. |
| 246 | Sk4f::Min(Sk4f::Max(c, zero), Sk4f::Min(c[3], one)).store(dstC + i); |
| 247 | c += dc; |
| 248 | } |
| 249 | } |
| 250 | |
| 251 | #ifndef SK_IGNORE_TO_STRING |
| 252 | void SkTriColorShader::toString(SkString* str) const { |
| 253 | str->append("SkTriColorShader: ("); |
| 254 | |
| 255 | this->INHERITED::toString(str); |
| 256 | |
| 257 | str->append(")"); |
| 258 | } |
| 259 | #endif |
| 260 | |
| 261 | |
| 262 | namespace { |
| 263 | |
| 264 | // Similar to SkLocalMatrixShader, but composes the local matrix with the CTM (instead |
| 265 | // of composing with the inherited local matrix): |
| 266 | // |
| 267 | // rec' = {rec.ctm x localMatrix, rec.localMatrix} |
| 268 | // |
| 269 | // (as opposed to rec' = {rec.ctm, rec.localMatrix x localMatrix}) |
| 270 | // |
| 271 | class SkLocalInnerMatrixShader final : public SkShader { |
| 272 | public: |
| 273 | SkLocalInnerMatrixShader(sk_sp<SkShader> proxy, const SkMatrix& localMatrix) |
| 274 | : INHERITED(&localMatrix) |
| 275 | , fProxyShader(std::move(proxy)) {} |
| 276 | |
| 277 | Factory getFactory() const override { |
| 278 | SkASSERT(false); |
| 279 | return nullptr; |
| 280 | } |
| 281 | |
| 282 | protected: |
| 283 | void flatten(SkWriteBuffer&) const override { |
| 284 | SkASSERT(false); |
| 285 | } |
| 286 | |
| 287 | Context* onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc) const override { |
| 288 | SkMatrix adjustedCTM = SkMatrix::Concat(*rec.fMatrix, this->getLocalMatrix()); |
| 289 | ContextRec newRec(rec); |
| 290 | newRec.fMatrix = &adjustedCTM; |
| 291 | return fProxyShader->makeContext(newRec, alloc); |
| 292 | } |
| 293 | |
| 294 | bool onAppendStages(SkRasterPipeline* p, SkColorSpace* cs, SkArenaAlloc* alloc, |
| 295 | const SkMatrix& ctm, const SkPaint& paint, |
| 296 | const SkMatrix* localM) const override { |
| 297 | // We control the shader graph ancestors, so we know there's no local matrix being |
| 298 | // injected before this. |
| 299 | SkASSERT(!localM); |
| 300 | |
| 301 | SkMatrix adjustedCTM = SkMatrix::Concat(ctm, this->getLocalMatrix()); |
| 302 | return fProxyShader->appendStages(p, cs, alloc, adjustedCTM, paint); |
| 303 | } |
| 304 | |
| 305 | private: |
| 306 | sk_sp<SkShader> fProxyShader; |
| 307 | |
| 308 | typedef SkShader INHERITED; |
| 309 | }; |
| 310 | |
| 311 | sk_sp<SkShader> MakeTextureShader(const VertState& state, const SkPoint verts[], |
| 312 | const SkPoint texs[], const SkPaint& paint, |
| 313 | SkColorSpace* dstColorSpace, |
| 314 | SkArenaAlloc* alloc) { |
| 315 | SkASSERT(paint.getShader()); |
| 316 | |
| 317 | const auto& p0 = texs[state.f0], |
| 318 | p1 = texs[state.f1], |
| 319 | p2 = texs[state.f2]; |
| 320 | |
| 321 | if (p0 != p1 || p0 != p2) { |
| 322 | // Common case (non-collapsed texture coordinates). |
| 323 | // Map the texture to vertices using a local transform. |
| 324 | |
| 325 | // We cannot use a plain SkLocalMatrix shader, because we need the texture matrix |
| 326 | // to compose next to the CTM. |
| 327 | SkMatrix localMatrix; |
| 328 | return texture_to_matrix(state, verts, texs, &localMatrix) |
| 329 | ? alloc->makeSkSp<SkLocalInnerMatrixShader>(paint.refShader(), localMatrix) |
| 330 | : nullptr; |
| 331 | } |
| 332 | |
| 333 | // Collapsed texture coordinates special case. |
| 334 | // The texture is a solid color, sampled at the given point. |
| 335 | SkMatrix shaderInvLocalMatrix; |
| 336 | SkAssertResult(paint.getShader()->getLocalMatrix().invert(&shaderInvLocalMatrix)); |
| 337 | |
| 338 | const auto sample = SkPoint::Make(0.5f, 0.5f); |
| 339 | const auto mappedSample = shaderInvLocalMatrix.mapXY(sample.x(), sample.y()), |
| 340 | mappedPoint = shaderInvLocalMatrix.mapXY(p0.x(), p0.y()); |
| 341 | const auto localMatrix = SkMatrix::MakeTrans(mappedSample.x() - mappedPoint.x(), |
| 342 | mappedSample.y() - mappedPoint.y()); |
| 343 | |
| 344 | SkShader::ContextRec rec(paint, SkMatrix::I(), &localMatrix, |
| 345 | SkShader::ContextRec::kPMColor_DstType, dstColorSpace); |
| 346 | auto* ctx = paint.getShader()->makeContext(rec, alloc); |
| 347 | if (!ctx) { |
| 348 | return nullptr; |
| 349 | } |
| 350 | |
| 351 | SkPMColor pmColor; |
| 352 | ctx->shadeSpan(SkScalarFloorToInt(sample.x()), SkScalarFloorToInt(sample.y()), &pmColor, 1); |
| 353 | |
| 354 | // no need to keep this temp context around. |
| 355 | alloc->reset(); |
| 356 | |
| 357 | return alloc->makeSkSp<SkColorShader>(SkUnPreMultiply::PMColorToColor(pmColor)); |
| 358 | } |
| 359 | |
| 360 | } // anonymous ns |
| 361 | |
| 362 | void SkDraw::drawVertices(SkVertices::VertexMode vmode, int count, |
| 363 | const SkPoint vertices[], const SkPoint textures[], |
| 364 | const SkColor colors[], SkBlendMode bmode, |
| 365 | const uint16_t indices[], int indexCount, |
| 366 | const SkPaint& paint) const { |
| 367 | SkASSERT(0 == count || vertices); |
| 368 | |
| 369 | // abort early if there is nothing to draw |
| 370 | if (count < 3 || (indices && indexCount < 3) || fRC->isEmpty()) { |
| 371 | return; |
| 372 | } |
| 373 | |
| 374 | // transform out vertices into device coordinates |
| 375 | SkAutoSTMalloc<16, SkPoint> storage(count); |
| 376 | SkPoint* devVerts = storage.get(); |
| 377 | fMatrix->mapPoints(devVerts, vertices, count); |
| 378 | |
| 379 | /* |
| 380 | We can draw the vertices in 1 of 4 ways: |
| 381 | |
| 382 | - solid color (no shader/texture[], no colors[]) |
| 383 | - just colors (no shader/texture[], has colors[]) |
| 384 | - just texture (has shader/texture[], no colors[]) |
| 385 | - colors * texture (has shader/texture[], has colors[]) |
| 386 | |
| 387 | Thus for texture drawing, we need both texture[] and a shader. |
| 388 | */ |
| 389 | |
| 390 | auto triShader = sk_make_sp<SkTriColorShader>(); |
| 391 | SkPaint p(paint); |
| 392 | |
| 393 | SkShader* shader = p.getShader(); |
| 394 | if (nullptr == shader) { |
| 395 | // if we have no shader, we ignore the texture coordinates |
| 396 | textures = nullptr; |
| 397 | } else if (nullptr == textures) { |
| 398 | // if we don't have texture coordinates, ignore the shader |
| 399 | p.setShader(nullptr); |
| 400 | shader = nullptr; |
| 401 | } |
| 402 | |
| 403 | // setup the custom shader (if needed) |
| 404 | if (colors) { |
| 405 | if (nullptr == textures) { |
| 406 | // just colors (no texture) |
| 407 | p.setShader(triShader); |
| 408 | } else { |
| 409 | // colors * texture |
| 410 | SkASSERT(shader); |
| 411 | p.setShader(SkShader::MakeComposeShader(triShader, sk_ref_sp(shader), bmode)); |
| 412 | } |
| 413 | } |
| 414 | |
| 415 | SkAutoBlitterChoose blitter(fDst, *fMatrix, p); |
| 416 | // Abort early if we failed to create a shader context. |
| 417 | if (blitter->isNullBlitter()) { |
| 418 | return; |
| 419 | } |
| 420 | |
| 421 | // setup our state and function pointer for iterating triangles |
| 422 | VertState state(count, indices, indexCount); |
| 423 | VertState::Proc vertProc = state.chooseProc(vmode); |
| 424 | |
| 425 | if (textures || colors) { |
| 426 | SkTriColorShader::TriColorShaderData verticesSetup = { vertices, colors, &state }; |
| 427 | |
| 428 | while (vertProc(&state)) { |
| 429 | auto* blitterPtr = blitter.get(); |
| 430 | |
| 431 | // We're going to allocate at most |
| 432 | // |
| 433 | // * one SkLocalMatrixShader OR one SkColorShader |
| 434 | // * one SkComposeShader |
| 435 | // * one SkAutoBlitterChoose |
| 436 | // |
| 437 | static constexpr size_t kAllocSize = |
| 438 | sizeof(SkAutoBlitterChoose) + sizeof(SkComposeShader) + |
| 439 | SkTMax(sizeof(SkLocalInnerMatrixShader), sizeof(SkColorShader)); |
| 440 | char allocBuffer[kAllocSize]; |
| 441 | SkArenaAlloc alloc(allocBuffer); |
| 442 | |
| 443 | if (textures) { |
| 444 | sk_sp<SkShader> texShader = MakeTextureShader(state, vertices, textures, paint, |
| 445 | fDst.colorSpace(), &alloc); |
| 446 | if (texShader) { |
| 447 | SkPaint localPaint(p); |
| 448 | localPaint.setShader(colors |
| 449 | ? alloc.makeSkSp<SkComposeShader>(triShader, std::move(texShader), bmode) |
| 450 | : std::move(texShader)); |
| 451 | |
| 452 | blitterPtr = alloc.make<SkAutoBlitterChoose>(fDst, *fMatrix, localPaint)->get(); |
| 453 | if (blitterPtr->isNullBlitter()) { |
| 454 | continue; |
| 455 | } |
| 456 | } |
| 457 | } |
| 458 | if (colors) { |
| 459 | triShader->bindSetupData(&verticesSetup); |
| 460 | } |
| 461 | |
| 462 | SkPoint tmp[] = { |
| 463 | devVerts[state.f0], devVerts[state.f1], devVerts[state.f2] |
| 464 | }; |
| 465 | SkScan::FillTriangle(tmp, *fRC, blitterPtr); |
| 466 | triShader->bindSetupData(nullptr); |
| 467 | } |
| 468 | } else { |
| 469 | // no colors[] and no texture, stroke hairlines with paint's color. |
| 470 | SkScan::HairRCProc hairProc = ChooseHairProc(paint.isAntiAlias()); |
| 471 | const SkRasterClip& clip = *fRC; |
| 472 | while (vertProc(&state)) { |
| 473 | SkPoint array[] = { |
| 474 | devVerts[state.f0], devVerts[state.f1], devVerts[state.f2], devVerts[state.f0] |
| 475 | }; |
| 476 | hairProc(array, 4, clip, blitter.get()); |
| 477 | } |
| 478 | } |
| 479 | } |