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 "include/core/SkString.h"
9 #include "include/private/SkNx.h"
10 #include "src/core/SkArenaAlloc.h"
11 #include "src/core/SkAutoBlitterChoose.h"
12 #include "src/core/SkConvertPixels.h"
13 #include "src/core/SkCoreBlitters.h"
14 #include "src/core/SkDraw.h"
15 #include "src/core/SkMatrixProvider.h"
16 #include "src/core/SkRasterClip.h"
17 #include "src/core/SkRasterPipeline.h"
18 #include "src/core/SkScan.h"
19 #include "src/core/SkVM.h"
20 #include "src/core/SkVMBlitter.h"
21 #include "src/core/SkVertState.h"
22 #include "src/core/SkVerticesPriv.h"
23 #include "src/shaders/SkComposeShader.h"
24 #include "src/shaders/SkShaderBase.h"
25
26 struct Matrix43 {
27 float fMat[12]; // column major
28
mapMatrix4329 Sk4f map(float x, float y) const {
30 return Sk4f::Load(&fMat[0]) * x + Sk4f::Load(&fMat[4]) * y + Sk4f::Load(&fMat[8]);
31 }
32
33 // Pass a by value, so we don't have to worry about aliasing with this
setConcatMatrix4334 void setConcat(const Matrix43 a, const SkMatrix& b) {
35 SkASSERT(!b.hasPerspective());
36
37 fMat[ 0] = a.dot(0, b.getScaleX(), b.getSkewY());
38 fMat[ 1] = a.dot(1, b.getScaleX(), b.getSkewY());
39 fMat[ 2] = a.dot(2, b.getScaleX(), b.getSkewY());
40 fMat[ 3] = a.dot(3, b.getScaleX(), b.getSkewY());
41
42 fMat[ 4] = a.dot(0, b.getSkewX(), b.getScaleY());
43 fMat[ 5] = a.dot(1, b.getSkewX(), b.getScaleY());
44 fMat[ 6] = a.dot(2, b.getSkewX(), b.getScaleY());
45 fMat[ 7] = a.dot(3, b.getSkewX(), b.getScaleY());
46
47 fMat[ 8] = a.dot(0, b.getTranslateX(), b.getTranslateY()) + a.fMat[ 8];
48 fMat[ 9] = a.dot(1, b.getTranslateX(), b.getTranslateY()) + a.fMat[ 9];
49 fMat[10] = a.dot(2, b.getTranslateX(), b.getTranslateY()) + a.fMat[10];
50 fMat[11] = a.dot(3, b.getTranslateX(), b.getTranslateY()) + a.fMat[11];
51 }
52
53 private:
dotMatrix4354 float dot(int index, float x, float y) const {
55 return fMat[index + 0] * x + fMat[index + 4] * y;
56 }
57 };
58
59 static bool SK_WARN_UNUSED_RESULT
texture_to_matrix(const VertState & state,const SkPoint verts[],const SkPoint texs[],SkMatrix * matrix)60 texture_to_matrix(const VertState& state, const SkPoint verts[], const SkPoint texs[],
61 SkMatrix* matrix) {
62 SkPoint src[3], dst[3];
63
64 src[0] = texs[state.f0];
65 src[1] = texs[state.f1];
66 src[2] = texs[state.f2];
67 dst[0] = verts[state.f0];
68 dst[1] = verts[state.f1];
69 dst[2] = verts[state.f2];
70 return matrix->setPolyToPoly(src, dst, 3);
71 }
72
73 class SkTriColorShader : public SkShaderBase {
74 public:
SkTriColorShader(bool isOpaque,bool usePersp)75 SkTriColorShader(bool isOpaque, bool usePersp) : fIsOpaque(isOpaque), fUsePersp(usePersp) {}
76
77 // This gets called for each triangle, without re-calling onAppendStages.
78 bool update(const SkMatrix& ctmInv, const SkPoint pts[], const SkPMColor4f colors[],
79 int index0, int index1, int index2);
80
81 protected:
82 #ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
onMakeContext(const ContextRec & rec,SkArenaAlloc * alloc) const83 Context* onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc) const override {
84 return nullptr;
85 }
86 #endif
onAppendStages(const SkStageRec & rec) const87 bool onAppendStages(const SkStageRec& rec) const override {
88 rec.fPipeline->append(SkRasterPipeline::seed_shader);
89 if (fUsePersp) {
90 rec.fPipeline->append(SkRasterPipeline::matrix_perspective, &fM33);
91 }
92 rec.fPipeline->append(SkRasterPipeline::matrix_4x3, &fM43);
93 return true;
94 }
95
96 skvm::Color onProgram(skvm::Builder*,
97 skvm::Coord, skvm::Coord, skvm::Color,
98 const SkMatrixProvider&, const SkMatrix*, const SkColorInfo&,
99 skvm::Uniforms*, SkArenaAlloc*) const override;
100
101 private:
isOpaque() const102 bool isOpaque() const override { return fIsOpaque; }
103 // For serialization. This will never be called.
getFactory() const104 Factory getFactory() const override { return nullptr; }
getTypeName() const105 const char* getTypeName() const override { return nullptr; }
106
107 // If fUsePersp, we need both of these matrices,
108 // otherwise we can combine them, and only use fM43
109
110 Matrix43 fM43;
111 SkMatrix fM33;
112 const bool fIsOpaque;
113 const bool fUsePersp; // controls our stages, and what we do in update()
114 mutable skvm::Uniform fColorMatrix;
115 mutable skvm::Uniform fCoordMatrix;
116
117 using INHERITED = SkShaderBase;
118 };
119
onProgram(skvm::Builder * b,skvm::Coord device,skvm::Coord local,skvm::Color,const SkMatrixProvider & matrices,const SkMatrix * localM,const SkColorInfo &,skvm::Uniforms * uniforms,SkArenaAlloc * alloc) const120 skvm::Color SkTriColorShader::onProgram(skvm::Builder* b,
121 skvm::Coord device, skvm::Coord local, skvm::Color,
122 const SkMatrixProvider& matrices, const SkMatrix* localM,
123 const SkColorInfo&, skvm::Uniforms* uniforms,
124 SkArenaAlloc* alloc) const {
125
126 fColorMatrix = uniforms->pushPtr(&fM43);
127
128 skvm::F32 x = local.x,
129 y = local.y;
130
131 if (fUsePersp) {
132 fCoordMatrix = uniforms->pushPtr(&fM33);
133 auto dot = [&, x, y](int row) {
134 return b->mad(x, b->arrayF(fCoordMatrix, row),
135 b->mad(y, b->arrayF(fCoordMatrix, row + 3),
136 b->arrayF(fCoordMatrix, row + 6)));
137 };
138
139 x = dot(0);
140 y = dot(1);
141 x = x * (1.0f / dot(2));
142 y = y * (1.0f / dot(2));
143 }
144
145 auto colorDot = [&, x, y](int row) {
146 return b->mad(x, b->arrayF(fColorMatrix, row),
147 b->mad(y, b->arrayF(fColorMatrix, row + 4),
148 b->arrayF(fColorMatrix, row + 8)));
149 };
150
151 skvm::Color color;
152 color.r = colorDot(0);
153 color.g = colorDot(1);
154 color.b = colorDot(2);
155 color.a = colorDot(3);
156 return color;
157 }
158
update(const SkMatrix & ctmInv,const SkPoint pts[],const SkPMColor4f colors[],int index0,int index1,int index2)159 bool SkTriColorShader::update(const SkMatrix& ctmInv, const SkPoint pts[],
160 const SkPMColor4f colors[], int index0, int index1, int index2) {
161 SkMatrix m, im;
162 m.reset();
163 m.set(0, pts[index1].fX - pts[index0].fX);
164 m.set(1, pts[index2].fX - pts[index0].fX);
165 m.set(2, pts[index0].fX);
166 m.set(3, pts[index1].fY - pts[index0].fY);
167 m.set(4, pts[index2].fY - pts[index0].fY);
168 m.set(5, pts[index0].fY);
169 if (!m.invert(&im)) {
170 return false;
171 }
172
173 fM33.setConcat(im, ctmInv);
174
175 Sk4f c0 = Sk4f::Load(colors[index0].vec()),
176 c1 = Sk4f::Load(colors[index1].vec()),
177 c2 = Sk4f::Load(colors[index2].vec());
178
179 (c1 - c0).store(&fM43.fMat[0]);
180 (c2 - c0).store(&fM43.fMat[4]);
181 c0.store(&fM43.fMat[8]);
182
183 if (!fUsePersp) {
184 fM43.setConcat(fM43, fM33);
185 }
186 return true;
187 }
188
189 // Convert the SkColors into float colors. The conversion depends on some conditions:
190 // - If the pixmap has a dst colorspace, we have to be "color-correct".
191 // Do we map into dst-colorspace before or after we interpolate?
192 // - We have to decide when to apply per-color alpha (before or after we interpolate)
193 //
194 // For now, we will take a simple approach, but recognize this is just a start:
195 // - convert colors into dst colorspace before interpolation (matches gradients)
196 // - apply per-color alpha before interpolation (matches old version of vertices)
197 //
convert_colors(const SkColor src[],int count,SkColorSpace * deviceCS,SkArenaAlloc * alloc)198 static SkPMColor4f* convert_colors(const SkColor src[], int count, SkColorSpace* deviceCS,
199 SkArenaAlloc* alloc) {
200 SkPMColor4f* dst = alloc->makeArray<SkPMColor4f>(count);
201 SkImageInfo srcInfo = SkImageInfo::Make(count, 1, kBGRA_8888_SkColorType,
202 kUnpremul_SkAlphaType, SkColorSpace::MakeSRGB());
203 SkImageInfo dstInfo = SkImageInfo::Make(count, 1, kRGBA_F32_SkColorType,
204 kPremul_SkAlphaType, sk_ref_sp(deviceCS));
205 SkAssertResult(SkConvertPixels(dstInfo, dst, 0, srcInfo, src, 0));
206 return dst;
207 }
208
compute_is_opaque(const SkColor colors[],int count)209 static bool compute_is_opaque(const SkColor colors[], int count) {
210 uint32_t c = ~0;
211 for (int i = 0; i < count; ++i) {
212 c &= colors[i];
213 }
214 return SkColorGetA(c) == 0xFF;
215 }
216
fill_triangle_2(const VertState & state,SkBlitter * blitter,const SkRasterClip & rc,const SkPoint dev2[])217 static void fill_triangle_2(const VertState& state, SkBlitter* blitter, const SkRasterClip& rc,
218 const SkPoint dev2[]) {
219 SkPoint tmp[] = {
220 dev2[state.f0], dev2[state.f1], dev2[state.f2]
221 };
222 SkScan::FillTriangle(tmp, rc, blitter);
223 }
224
225 static constexpr int kMaxClippedTrianglePointCount = 4;
fill_triangle_3(const VertState & state,SkBlitter * blitter,const SkRasterClip & rc,const SkPoint3 dev3[])226 static void fill_triangle_3(const VertState& state, SkBlitter* blitter, const SkRasterClip& rc,
227 const SkPoint3 dev3[]) {
228 // Compute the crossing point (across zero) for the two values, expressed as a
229 // normalized 0...1 value. If curr is 0, returns 0. If next is 0, returns 1.
230 auto computeT = [](float curr, float next) {
231 // Check that 0 is between next and curr.
232 SkASSERT((next <= 0 && 0 < curr) || (curr <= 0 && 0 < next));
233 float t = curr / (curr - next);
234 SkASSERT(0 <= t && t <= 1);
235 return t;
236 };
237
238 auto lerp = [](SkPoint3 curr, SkPoint3 next, float t) {
239 return curr + t * (next - curr);
240 };
241
242 constexpr float tol = 0.05f;
243 // tol is the nudge away from zero, to keep the numerics nice.
244 // Think of it as our near-clipping-plane (or w-plane).
245 auto clip = [&](SkPoint3 curr, SkPoint3 next) {
246 // Return the point between curr and next where the fZ value crosses tol.
247 // To be (really) perspective correct, we should be computing based on 1/Z, not Z.
248 // For now, this is close enough (and faster).
249 return lerp(curr, next, computeT(curr.fZ - tol, next.fZ - tol));
250 };
251
252 // Clip a triangle (based on its homogeneous W values), and return the projected polygon.
253 // Since we only clip against one "edge"/plane, the max number of points in the clipped
254 // polygon is 4.
255 auto clipTriangle = [&](SkPoint dst[], const int idx[3], const SkPoint3 pts[]) -> int {
256 SkPoint3 outPoints[kMaxClippedTrianglePointCount];
257 SkPoint3* outP = outPoints;
258
259 for (int i = 0; i < 3; ++i) {
260 int curr = idx[i];
261 int next = idx[(i + 1) % 3];
262 if (pts[curr].fZ > tol) {
263 *outP++ = pts[curr];
264 if (pts[next].fZ <= tol) { // curr is IN, next is OUT
265 *outP++ = clip(pts[curr], pts[next]);
266 }
267 } else {
268 if (pts[next].fZ > tol) { // curr is OUT, next is IN
269 *outP++ = clip(pts[curr], pts[next]);
270 }
271 }
272 }
273
274 const int count = SkTo<int>(outP - outPoints);
275 SkASSERT(count == 0 || count == 3 || count == 4);
276 for (int i = 0; i < count; ++i) {
277 float scale = 1.0f / outPoints[i].fZ;
278 dst[i].set(outPoints[i].fX * scale, outPoints[i].fY * scale);
279 }
280 return count;
281 };
282
283 SkPoint tmp[kMaxClippedTrianglePointCount];
284 int idx[] = { state.f0, state.f1, state.f2 };
285 if (int n = clipTriangle(tmp, idx, dev3)) {
286 // TODO: SkScan::FillConvexPoly(tmp, n, ...);
287 SkASSERT(n == 3 || n == 4);
288 SkScan::FillTriangle(tmp, rc, blitter);
289 if (n == 4) {
290 tmp[1] = tmp[2];
291 tmp[2] = tmp[3];
292 SkScan::FillTriangle(tmp, rc, blitter);
293 }
294 }
295 }
296
fill_triangle(const VertState & state,SkBlitter * blitter,const SkRasterClip & rc,const SkPoint dev2[],const SkPoint3 dev3[])297 static void fill_triangle(const VertState& state, SkBlitter* blitter, const SkRasterClip& rc,
298 const SkPoint dev2[], const SkPoint3 dev3[]) {
299 if (dev3) {
300 fill_triangle_3(state, blitter, rc, dev3);
301 } else {
302 fill_triangle_2(state, blitter, rc, dev2);
303 }
304 }
305
306 extern bool gUseSkVMBlitter;
307
drawFixedVertices(const SkVertices * vertices,SkBlendMode blendMode,const SkPaint & paint,const SkMatrix & ctmInverse,const SkPoint * dev2,const SkPoint3 * dev3,SkArenaAlloc * outerAlloc) const308 void SkDraw::drawFixedVertices(const SkVertices* vertices, SkBlendMode blendMode,
309 const SkPaint& paint, const SkMatrix& ctmInverse,
310 const SkPoint* dev2, const SkPoint3* dev3,
311 SkArenaAlloc* outerAlloc) const {
312 SkVerticesPriv info(vertices->priv());
313
314 const int vertexCount = info.vertexCount();
315 const int indexCount = info.indexCount();
316 const SkPoint* positions = info.positions();
317 const SkPoint* texCoords = info.texCoords();
318 const uint16_t* indices = info.indices();
319 const SkColor* colors = info.colors();
320
321 SkShader* paintShader = paint.getShader();
322
323 if (paintShader) {
324 if (!texCoords) {
325 texCoords = positions;
326 }
327 } else {
328 texCoords = nullptr;
329 }
330
331 // We can simplify things for certain blend modes. This is for speed, and SkComposeShader
332 // itself insists we don't pass kSrc or kDst to it.
333 if (colors && texCoords) {
334 switch (blendMode) {
335 case SkBlendMode::kSrc:
336 colors = nullptr;
337 break;
338 case SkBlendMode::kDst:
339 texCoords = nullptr;
340 paintShader = nullptr;
341 break;
342 default: break;
343 }
344 }
345
346 // There is a paintShader iff there is texCoords.
347 SkASSERT((texCoords != nullptr) == (paintShader != nullptr));
348
349 SkMatrix ctm = fMatrixProvider->localToDevice();
350 const bool usePerspective = ctm.hasPerspective();
351
352 auto rpblit = [&]() {
353 VertState state(vertexCount, indices, indexCount);
354 VertState::Proc vertProc = state.chooseProc(info.mode());
355
356 SkShader* shader = paintShader;
357 SkTriColorShader* triShader = nullptr;
358 SkPMColor4f* dstColors = nullptr;
359
360 if (colors) {
361 dstColors = convert_colors(colors, vertexCount, fDst.colorSpace(), outerAlloc);
362 triShader = outerAlloc->make<SkTriColorShader>(compute_is_opaque(colors, vertexCount),
363 usePerspective);
364 if (shader) {
365 shader = outerAlloc->make<SkShader_Blend>(
366 blendMode, sk_ref_sp(triShader), sk_ref_sp(shader));
367 } else {
368 shader = triShader;
369 }
370 }
371
372 SkPaint shaderPaint(paint);
373 shaderPaint.setShader(sk_ref_sp(shader));
374
375 if (!texCoords) { // only tricolor shader
376 auto blitter = SkCreateRasterPipelineBlitter(
377 fDst, shaderPaint, *fMatrixProvider, outerAlloc, this->fRC->clipShader());
378 if (!blitter) {
379 return false;
380 }
381 while (vertProc(&state)) {
382 if (triShader &&
383 !triShader->update(ctmInverse, positions, dstColors,
384 state.f0, state.f1, state.f2)) {
385 continue;
386 }
387 fill_triangle(state, blitter, *fRC, dev2, dev3);
388 }
389 return true;
390 }
391
392 SkRasterPipeline pipeline(outerAlloc);
393 SkStageRec rec = {&pipeline,
394 outerAlloc,
395 fDst.colorType(),
396 fDst.colorSpace(),
397 shaderPaint,
398 nullptr,
399 *fMatrixProvider};
400 if (auto updater = as_SB(shader)->appendUpdatableStages(rec)) {
401 bool isOpaque = shader->isOpaque();
402 if (triShader) {
403 isOpaque = false; // unless we want to walk all the colors, and see if they are
404 // all opaque (and the blend mode will keep them that way
405 }
406
407 // Positions as texCoords? The local matrix is always identity, so update once
408 if (texCoords == positions) {
409 if (!updater->update(ctm)) {
410 return true;
411 }
412 }
413
414 auto blitter = SkCreateRasterPipelineBlitter(
415 fDst, shaderPaint, pipeline, isOpaque, outerAlloc, fRC->clipShader());
416 if (!blitter) {
417 return false;
418 }
419 while (vertProc(&state)) {
420 if (triShader && !triShader->update(ctmInverse, positions, dstColors,
421 state.f0, state.f1, state.f2)) {
422 continue;
423 }
424
425 SkMatrix localM;
426 if ((texCoords == positions) ||
427 (texture_to_matrix(state, positions, texCoords, &localM) &&
428 updater->update(SkMatrix::Concat(ctm, localM)))) {
429 fill_triangle(state, blitter, *fRC, dev2, dev3);
430 }
431 }
432 } else {
433 // must rebuild pipeline for each triangle, to pass in the computed ctm
434 while (vertProc(&state)) {
435 if (triShader && !triShader->update(ctmInverse, positions, dstColors,
436 state.f0, state.f1, state.f2)) {
437 continue;
438 }
439
440 SkSTArenaAlloc<2048> innerAlloc;
441
442 const SkMatrixProvider* matrixProvider = fMatrixProvider;
443 SkTLazy<SkPreConcatMatrixProvider> preConcatMatrixProvider;
444 if (texCoords && (texCoords != positions)) {
445 SkMatrix localM;
446 if (!texture_to_matrix(state, positions, texCoords, &localM)) {
447 continue;
448 }
449 matrixProvider = preConcatMatrixProvider.init(*matrixProvider, localM);
450 }
451
452 // It'd be nice if we could detect this will fail earlier.
453 auto blitter = SkCreateRasterPipelineBlitter(
454 fDst, shaderPaint, *matrixProvider, &innerAlloc, this->fRC->clipShader());
455 if (!blitter) {
456 return false;
457 }
458 fill_triangle(state, blitter, *fRC, dev2, dev3);
459 }
460 }
461 return true;
462 };
463
464 if (gUseSkVMBlitter || !rpblit()) {
465 VertState state(vertexCount, indices, indexCount);
466 VertState::Proc vertProc = state.chooseProc(info.mode());
467
468 // No colors are changing and no texture coordinates are changing, so no updates between
469 // triangles are needed. Use SkVM to blit the triangles.
470 SkShader* shader = paintShader;
471 SkUpdatableShader* texCoordShader = nullptr;
472 if (texCoords && texCoords != positions) {
473 texCoordShader = as_SB(shader)->updatableShader(outerAlloc);
474 shader = texCoordShader;
475 }
476
477 SkTriColorShader* colorShader = nullptr;
478 SkPMColor4f* dstColors = nullptr;
479 if (colors) {
480 colorShader = outerAlloc->make<SkTriColorShader>(compute_is_opaque(colors, vertexCount),
481 usePerspective);
482 dstColors = convert_colors(colors, vertexCount, fDst.colorSpace(), outerAlloc);
483 if (shader) {
484 shader = outerAlloc->make<SkShader_Blend>(
485 blendMode, sk_ref_sp(colorShader), sk_ref_sp(shader));
486 } else {
487 shader = colorShader;
488 }
489 }
490
491 SkPaint shaderPaint{paint};
492 shaderPaint.setShader(sk_ref_sp(shader));
493 auto blitter = SkVMBlitter::Make(
494 fDst, shaderPaint, *fMatrixProvider, outerAlloc, this->fRC->clipShader());
495 if (!blitter) {
496 return;
497 }
498 while (vertProc(&state)) {
499 SkMatrix localM;
500 if (texCoordShader && !(texture_to_matrix(state, positions, texCoords, &localM) &&
501 texCoordShader->update(SkMatrix::Concat(ctm, localM)))) {
502 continue;
503 }
504
505 if (colorShader && !colorShader->update(ctmInverse, positions, dstColors,state.f0,
506 state.f1, state.f2)) {
507 continue;
508 }
509
510 fill_triangle(state, blitter, *fRC, dev2, dev3);
511 }
512 }
513 }
514
drawVertices(const SkVertices * vertices,SkBlendMode bmode,const SkPaint & paint) const515 void SkDraw::drawVertices(const SkVertices* vertices, SkBlendMode bmode,
516 const SkPaint& paint) const {
517 SkVerticesPriv info(vertices->priv());
518 const int vertexCount = info.vertexCount();
519 const int indexCount = info.indexCount();
520
521 // abort early if there is nothing to draw
522 if (vertexCount < 3 || (indexCount > 0 && indexCount < 3) || fRC->isEmpty()) {
523 return;
524 }
525 SkMatrix ctm = fMatrixProvider->localToDevice();
526 SkMatrix ctmInv;
527 if (!ctm.invert(&ctmInv)) {
528 return;
529 }
530
531 constexpr size_t kDefVertexCount = 16;
532 constexpr size_t kOuterSize = sizeof(SkTriColorShader) +
533 sizeof(SkShader_Blend) +
534 (2 * sizeof(SkPoint) + sizeof(SkColor4f)) * kDefVertexCount;
535 SkSTArenaAlloc<kOuterSize> outerAlloc;
536
537 SkPoint* dev2 = nullptr;
538 SkPoint3* dev3 = nullptr;
539
540 if (ctm.hasPerspective()) {
541 dev3 = outerAlloc.makeArray<SkPoint3>(vertexCount);
542 ctm.mapHomogeneousPoints(dev3, info.positions(), vertexCount);
543 // similar to the bounds check for 2d points (below)
544 if (!SkScalarsAreFinite((const SkScalar*)dev3, vertexCount * 3)) {
545 return;
546 }
547 } else {
548 dev2 = outerAlloc.makeArray<SkPoint>(vertexCount);
549 ctm.mapPoints(dev2, info.positions(), vertexCount);
550
551 SkRect bounds;
552 // this also sets bounds to empty if we see a non-finite value
553 bounds.setBounds(dev2, vertexCount);
554 if (bounds.isEmpty()) {
555 return;
556 }
557 }
558
559 this->drawFixedVertices(vertices, bmode, paint, ctmInv, dev2, dev3, &outerAlloc);
560 }
561