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/SkRasterClip.h"
16 #include "src/core/SkRasterPipeline.h"
17 #include "src/core/SkScan.h"
18 #include "src/core/SkVertState.h"
19 #include "src/shaders/SkComposeShader.h"
20 #include "src/shaders/SkShaderBase.h"
21
22 // Compute the crossing point (across zero) for the two values, expressed as a
23 // normalized 0...1 value. If curr is 0, returns 0. If next is 0, returns 1.
24 //
compute_t(float curr,float next)25 static float compute_t(float curr, float next) {
26 SkASSERT((curr > 0 && next <= 0) || (curr <= 0 && next > 0));
27 float t = curr / (curr - next);
28 SkASSERT(t >= 0 && t <= 1);
29 return t;
30 }
31
lerp(SkPoint3 curr,SkPoint3 next,float t)32 static SkPoint3 lerp(SkPoint3 curr, SkPoint3 next, float t) {
33 return curr + t * (next - curr);
34 }
35
36 // tol is the nudge away from zero, to keep the numerics nice.
37 // Think of it as our near-clipping-plane (or w-plane).
clip(SkPoint3 curr,SkPoint3 next,float tol)38 static SkPoint3 clip(SkPoint3 curr, SkPoint3 next, float tol) {
39 // Return the point between curr and next where the fZ value corses tol.
40 // To be (really) perspective correct, we should be computing baesd on 1/Z, not Z.
41 // For now, this is close enough (and faster).
42 return lerp(curr, next, compute_t(curr.fZ - tol, next.fZ - tol));
43 }
44
45 constexpr int kMaxClippedTrianglePointCount = 4;
46 // Clip a triangle (based on its homogeneous W values), and return the projected polygon.
47 // Since we only clip against one "edge"/plane, the max number of points in the clipped
48 // polygon is 4.
clip_triangle(SkPoint dst[],const int idx[3],const SkPoint3 pts[])49 static int clip_triangle(SkPoint dst[], const int idx[3], const SkPoint3 pts[]) {
50 SkPoint3 outPoints[4];
51 SkPoint3* outP = outPoints;
52 const float tol = 0.05f;
53
54 for (int i = 0; i < 3; ++i) {
55 int curr = idx[i];
56 int next = idx[(i + 1) % 3];
57 if (pts[curr].fZ > tol) {
58 *outP++ = pts[curr];
59 if (pts[next].fZ <= tol) { // curr is IN, next is OUT
60 *outP++ = clip(pts[curr], pts[next], tol);
61 }
62 } else {
63 if (pts[next].fZ > tol) { // curr is OUT, next is IN
64 *outP++ = clip(pts[curr], pts[next], tol);
65 }
66 }
67 }
68
69 const int count = outP - outPoints;
70 SkASSERT(count == 0 || count == 3 || count == 4);
71 for (int i = 0; i < count; ++i) {
72 float scale = 1.0f / outPoints[i].fZ;
73 dst[i].set(outPoints[i].fX * scale, outPoints[i].fY * scale);
74 }
75 return count;
76 }
77
78 struct Matrix43 {
79 float fMat[12]; // column major
80
mapMatrix4381 Sk4f map(float x, float y) const {
82 return Sk4f::Load(&fMat[0]) * x + Sk4f::Load(&fMat[4]) * y + Sk4f::Load(&fMat[8]);
83 }
84
85 // Pass a by value, so we don't have to worry about aliasing with this
setConcatMatrix4386 void setConcat(const Matrix43 a, const SkMatrix& b) {
87 SkASSERT(!b.hasPerspective());
88
89 fMat[ 0] = a.dot(0, b.getScaleX(), b.getSkewY());
90 fMat[ 1] = a.dot(1, b.getScaleX(), b.getSkewY());
91 fMat[ 2] = a.dot(2, b.getScaleX(), b.getSkewY());
92 fMat[ 3] = a.dot(3, b.getScaleX(), b.getSkewY());
93
94 fMat[ 4] = a.dot(0, b.getSkewX(), b.getScaleY());
95 fMat[ 5] = a.dot(1, b.getSkewX(), b.getScaleY());
96 fMat[ 6] = a.dot(2, b.getSkewX(), b.getScaleY());
97 fMat[ 7] = a.dot(3, b.getSkewX(), b.getScaleY());
98
99 fMat[ 8] = a.dot(0, b.getTranslateX(), b.getTranslateY()) + a.fMat[ 8];
100 fMat[ 9] = a.dot(1, b.getTranslateX(), b.getTranslateY()) + a.fMat[ 9];
101 fMat[10] = a.dot(2, b.getTranslateX(), b.getTranslateY()) + a.fMat[10];
102 fMat[11] = a.dot(3, b.getTranslateX(), b.getTranslateY()) + a.fMat[11];
103 }
104
105 private:
dotMatrix43106 float dot(int index, float x, float y) const {
107 return fMat[index + 0] * x + fMat[index + 4] * y;
108 }
109 };
110
ChooseHairProc(bool doAntiAlias)111 static SkScan::HairRCProc ChooseHairProc(bool doAntiAlias) {
112 return doAntiAlias ? SkScan::AntiHairLine : SkScan::HairLine;
113 }
114
115 static bool SK_WARN_UNUSED_RESULT
texture_to_matrix(const VertState & state,const SkPoint verts[],const SkPoint texs[],SkMatrix * matrix)116 texture_to_matrix(const VertState& state, const SkPoint verts[], const SkPoint texs[],
117 SkMatrix* matrix) {
118 SkPoint src[3], dst[3];
119
120 src[0] = texs[state.f0];
121 src[1] = texs[state.f1];
122 src[2] = texs[state.f2];
123 dst[0] = verts[state.f0];
124 dst[1] = verts[state.f1];
125 dst[2] = verts[state.f2];
126 return matrix->setPolyToPoly(src, dst, 3);
127 }
128
129 class SkTriColorShader : public SkShaderBase {
130 public:
SkTriColorShader(bool isOpaque,bool usePersp)131 SkTriColorShader(bool isOpaque, bool usePersp) : fIsOpaque(isOpaque), fUsePersp(usePersp) {}
132
133 // This gets called for each triangle, without re-calling onAppendStages.
134 bool update(const SkMatrix& ctmInv, const SkPoint pts[], const SkPMColor4f colors[],
135 int index0, int index1, int index2);
136
137 protected:
138 #ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
onMakeContext(const ContextRec & rec,SkArenaAlloc * alloc) const139 Context* onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc) const override {
140 return nullptr;
141 }
142 #endif
onAppendStages(const SkStageRec & rec) const143 bool onAppendStages(const SkStageRec& rec) const override {
144 rec.fPipeline->append(SkRasterPipeline::seed_shader);
145 if (fUsePersp) {
146 rec.fPipeline->append(SkRasterPipeline::matrix_perspective, &fM33);
147 }
148 rec.fPipeline->append(SkRasterPipeline::matrix_4x3, &fM43);
149 return true;
150 }
151
152 private:
isOpaque() const153 bool isOpaque() const override { return fIsOpaque; }
154 // For serialization. This will never be called.
getFactory() const155 Factory getFactory() const override { return nullptr; }
getTypeName() const156 const char* getTypeName() const override { return nullptr; }
157
158 // If fUsePersp, we need both of these matrices,
159 // otherwise we can combine them, and only use fM43
160
161 Matrix43 fM43;
162 SkMatrix fM33;
163 const bool fIsOpaque;
164 const bool fUsePersp; // controls our stages, and what we do in update()
165
166 typedef SkShaderBase INHERITED;
167 };
168
update(const SkMatrix & ctmInv,const SkPoint pts[],const SkPMColor4f colors[],int index0,int index1,int index2)169 bool SkTriColorShader::update(const SkMatrix& ctmInv, const SkPoint pts[],
170 const SkPMColor4f colors[], int index0, int index1, int index2) {
171 SkMatrix m, im;
172 m.reset();
173 m.set(0, pts[index1].fX - pts[index0].fX);
174 m.set(1, pts[index2].fX - pts[index0].fX);
175 m.set(2, pts[index0].fX);
176 m.set(3, pts[index1].fY - pts[index0].fY);
177 m.set(4, pts[index2].fY - pts[index0].fY);
178 m.set(5, pts[index0].fY);
179 if (!m.invert(&im)) {
180 return false;
181 }
182
183 fM33.setConcat(im, ctmInv);
184
185 Sk4f c0 = Sk4f::Load(colors[index0].vec()),
186 c1 = Sk4f::Load(colors[index1].vec()),
187 c2 = Sk4f::Load(colors[index2].vec());
188
189 (c1 - c0).store(&fM43.fMat[0]);
190 (c2 - c0).store(&fM43.fMat[4]);
191 c0.store(&fM43.fMat[8]);
192
193 if (!fUsePersp) {
194 fM43.setConcat(fM43, fM33);
195 }
196 return true;
197 }
198
199 // Convert the SkColors into float colors. The conversion depends on some conditions:
200 // - If the pixmap has a dst colorspace, we have to be "color-correct".
201 // Do we map into dst-colorspace before or after we interpolate?
202 // - We have to decide when to apply per-color alpha (before or after we interpolate)
203 //
204 // For now, we will take a simple approach, but recognize this is just a start:
205 // - convert colors into dst colorspace before interpolation (matches gradients)
206 // - apply per-color alpha before interpolation (matches old version of vertices)
207 //
convert_colors(const SkColor src[],int count,SkColorSpace * deviceCS,SkArenaAlloc * alloc)208 static SkPMColor4f* convert_colors(const SkColor src[], int count, SkColorSpace* deviceCS,
209 SkArenaAlloc* alloc) {
210 SkPMColor4f* dst = alloc->makeArray<SkPMColor4f>(count);
211 SkImageInfo srcInfo = SkImageInfo::Make(count, 1, kBGRA_8888_SkColorType,
212 kUnpremul_SkAlphaType, SkColorSpace::MakeSRGB());
213 SkImageInfo dstInfo = SkImageInfo::Make(count, 1, kRGBA_F32_SkColorType,
214 kPremul_SkAlphaType, sk_ref_sp(deviceCS));
215 SkConvertPixels(dstInfo, dst, 0, srcInfo, src, 0);
216 return dst;
217 }
218
compute_is_opaque(const SkColor colors[],int count)219 static bool compute_is_opaque(const SkColor colors[], int count) {
220 uint32_t c = ~0;
221 for (int i = 0; i < count; ++i) {
222 c &= colors[i];
223 }
224 return SkColorGetA(c) == 0xFF;
225 }
226
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 SkVertices::Bone bones[],int boneCount) const227 void SkDraw::drawVertices(SkVertices::VertexMode vmode, int vertexCount,
228 const SkPoint vertices[], const SkPoint textures[],
229 const SkColor colors[], const SkVertices::BoneIndices boneIndices[],
230 const SkVertices::BoneWeights boneWeights[], SkBlendMode bmode,
231 const uint16_t indices[], int indexCount,
232 const SkPaint& paint, const SkVertices::Bone bones[],
233 int boneCount) const {
234 SkASSERT(0 == vertexCount || vertices);
235
236 // abort early if there is nothing to draw
237 if (vertexCount < 3 || (indices && indexCount < 3) || fRC->isEmpty()) {
238 return;
239 }
240 SkMatrix ctmInv;
241 if (!fMatrix->invert(&ctmInv)) {
242 return;
243 }
244
245 // make textures and shader mutually consistent
246 SkShader* shader = paint.getShader();
247 if (!(shader && textures)) {
248 shader = nullptr;
249 textures = nullptr;
250 }
251
252 // We can simplify things for certain blendmodes. This is for speed, and SkComposeShader
253 // itself insists we don't pass kSrc or kDst to it.
254 //
255 if (colors && textures) {
256 switch (bmode) {
257 case SkBlendMode::kSrc:
258 colors = nullptr;
259 break;
260 case SkBlendMode::kDst:
261 textures = nullptr;
262 break;
263 default: break;
264 }
265 }
266
267 // we don't use the shader if there are no textures
268 if (!textures) {
269 shader = nullptr;
270 }
271
272 constexpr size_t kDefVertexCount = 16;
273 constexpr size_t kOuterSize = sizeof(SkTriColorShader) +
274 sizeof(SkShader_Blend) +
275 (2 * sizeof(SkPoint) + sizeof(SkColor4f)) * kDefVertexCount;
276 SkSTArenaAlloc<kOuterSize> outerAlloc;
277
278 // deform vertices using the skeleton if it is passed in
279 if (bones && boneCount) {
280 // allocate space for the deformed vertices
281 SkPoint* deformed = outerAlloc.makeArray<SkPoint>(vertexCount);
282
283 // deform the vertices
284 if (boneIndices && boneWeights) {
285 for (int i = 0; i < vertexCount; i ++) {
286 const SkVertices::BoneIndices& indices = boneIndices[i];
287 const SkVertices::BoneWeights& weights = boneWeights[i];
288
289 // apply the world transform
290 SkPoint worldPoint = bones[0].mapPoint(vertices[i]);
291
292 // apply bone deformations
293 deformed[i] = SkPoint::Make(0.0f, 0.0f);
294 for (uint32_t j = 0; j < 4; j ++) {
295 // get the attachment data
296 uint32_t index = indices[j];
297 float weight = weights[j];
298
299 // skip the bone if there is no weight
300 if (weight == 0.0f) {
301 continue;
302 }
303 SkASSERT(index != 0);
304
305 // deformed += M * v * w
306 deformed[i] += bones[index].mapPoint(worldPoint) * weight;
307 }
308 }
309 } else {
310 // no bones, so only apply world transform
311 SkMatrix worldTransform = SkMatrix::I();
312 worldTransform.setAffine(bones[0].values);
313 worldTransform.mapPoints(deformed, vertices, vertexCount);
314 }
315
316 // change the vertices to point to deformed
317 vertices = deformed;
318 }
319
320 /* We need to know if we have perspective or not, so we can know what stage(s) we will need,
321 and how to prep our "uniforms" before each triangle in the tricolorshader.
322
323 We could just check the matrix on each triangle to decide, but we have to be sure to always
324 make the same decision, since we create 1 or 2 stages only once for the entire patch.
325
326 To be safe, we just make that determination here, and pass it into the tricolorshader.
327 */
328 const bool usePerspective = fMatrix->hasPerspective();
329
330 SkPoint* devVerts = nullptr;
331 SkPoint3* dev3 = nullptr;
332
333 if (usePerspective) {
334 dev3 = outerAlloc.makeArray<SkPoint3>(vertexCount);
335 fMatrix->mapHomogeneousPoints(dev3, vertices, vertexCount);
336 // similar to the bounds check for 2d points (below)
337 if (!SkScalarsAreFinite((const SkScalar*)dev3, vertexCount * 3)) {
338 return;
339 }
340 } else {
341 devVerts = outerAlloc.makeArray<SkPoint>(vertexCount);
342 fMatrix->mapPoints(devVerts, vertices, vertexCount);
343
344 SkRect bounds;
345 // this also sets bounds to empty if we see a non-finite value
346 bounds.setBounds(devVerts, vertexCount);
347 if (bounds.isEmpty()) {
348 return;
349 }
350 }
351
352 VertState state(vertexCount, indices, indexCount);
353 VertState::Proc vertProc = state.chooseProc(vmode);
354
355 // Draw hairlines to show the skeleton
356 if (!(colors || textures)) {
357 // no colors[] and no texture, stroke hairlines with paint's color.
358 SkPaint p;
359 p.setStyle(SkPaint::kStroke_Style);
360 SkAutoBlitterChoose blitter(*this, nullptr, p);
361 // Abort early if we failed to create a shader context.
362 if (blitter->isNullBlitter()) {
363 return;
364 }
365 SkScan::HairRCProc hairProc = ChooseHairProc(paint.isAntiAlias());
366 const SkRasterClip& clip = *fRC;
367 while (vertProc(&state)) {
368 if (dev3) {
369 SkPoint tmp[kMaxClippedTrianglePointCount + 2];
370 int idx[] = { state.f0, state.f1, state.f2 };
371 if (int n = clip_triangle(tmp, idx, dev3)) {
372 tmp[n] = tmp[0]; // close the poly
373 if (n == 3) {
374 n = 4;
375 } else {
376 SkASSERT(n == 4);
377 tmp[5] = tmp[2]; // add diagonal
378 n = 6;
379 }
380 hairProc(tmp, n, clip, blitter.get());
381 }
382 } else {
383 SkPoint array[] = {
384 devVerts[state.f0], devVerts[state.f1], devVerts[state.f2], devVerts[state.f0]
385 };
386 hairProc(array, 4, clip, blitter.get());
387 }
388 }
389 return;
390 }
391
392 SkTriColorShader* triShader = nullptr;
393 SkPMColor4f* dstColors = nullptr;
394
395 if (colors) {
396 dstColors = convert_colors(colors, vertexCount, fDst.colorSpace(), &outerAlloc);
397 triShader = outerAlloc.make<SkTriColorShader>(compute_is_opaque(colors, vertexCount),
398 usePerspective);
399 if (shader) {
400 shader = outerAlloc.make<SkShader_Blend>(bmode,
401 sk_ref_sp(triShader), sk_ref_sp(shader),
402 nullptr);
403 } else {
404 shader = triShader;
405 }
406 }
407
408 auto handle_devVerts = [&](SkBlitter* blitter) {
409 SkPoint tmp[] = {
410 devVerts[state.f0], devVerts[state.f1], devVerts[state.f2]
411 };
412 SkScan::FillTriangle(tmp, *fRC, blitter);
413 };
414
415 auto handle_dev3 = [&](SkBlitter* blitter) {
416 SkPoint tmp[kMaxClippedTrianglePointCount];
417 int idx[] = { state.f0, state.f1, state.f2 };
418 if (int n = clip_triangle(tmp, idx, dev3)) {
419 // TODO: SkScan::FillConvexPoly(tmp, n, ...);
420 SkASSERT(n == 3 || n == 4);
421 SkScan::FillTriangle(tmp, *fRC, blitter);
422 if (n == 4) {
423 tmp[1] = tmp[2];
424 tmp[2] = tmp[3];
425 SkScan::FillTriangle(tmp, *fRC, blitter);
426 }
427 }
428 };
429
430 SkPaint p(paint);
431 p.setShader(sk_ref_sp(shader));
432
433 if (!textures) { // only tricolor shader
434 auto blitter = SkCreateRasterPipelineBlitter(fDst, p, *fMatrix, &outerAlloc);
435 while (vertProc(&state)) {
436 if (!triShader->update(ctmInv, vertices, dstColors, state.f0, state.f1, state.f2)) {
437 continue;
438 }
439
440 if (dev3) {
441 handle_dev3(blitter);
442 } else {
443 handle_devVerts(blitter);
444 }
445 }
446 return;
447 }
448
449 SkRasterPipeline pipeline(&outerAlloc);
450 SkStageRec rec = {
451 &pipeline, &outerAlloc, fDst.colorType(), fDst.colorSpace(), p, nullptr, *fMatrix
452 };
453 if (auto updater = as_SB(shader)->appendUpdatableStages(rec)) {
454 bool isOpaque = shader->isOpaque();
455 if (triShader) {
456 isOpaque = false; // unless we want to walk all the colors, and see if they are
457 // all opaque (and the blendmode will keep them that way
458 }
459
460 auto blitter = SkCreateRasterPipelineBlitter(fDst, p, pipeline, isOpaque, &outerAlloc);
461 while (vertProc(&state)) {
462 if (triShader && !triShader->update(ctmInv, vertices, dstColors,
463 state.f0, state.f1, state.f2)) {
464 continue;
465 }
466
467 SkMatrix localM;
468 if (!texture_to_matrix(state, vertices, textures, &localM) ||
469 !updater->update(*fMatrix, &localM)) {
470 continue;
471 }
472
473 if (dev3) {
474 handle_dev3(blitter);
475 } else {
476 handle_devVerts(blitter);
477 }
478 }
479 } else {
480 // must rebuild pipeline for each triangle, to pass in the computed ctm
481 while (vertProc(&state)) {
482 if (triShader && !triShader->update(ctmInv, vertices, dstColors,
483 state.f0, state.f1, state.f2)) {
484 continue;
485 }
486
487 SkSTArenaAlloc<2048> innerAlloc;
488
489 const SkMatrix* ctm = fMatrix;
490 SkMatrix tmpCtm;
491 if (textures) {
492 SkMatrix localM;
493 if (!texture_to_matrix(state, vertices, textures, &localM)) {
494 continue;
495 }
496 tmpCtm = SkMatrix::Concat(*fMatrix, localM);
497 ctm = &tmpCtm;
498 }
499
500 auto blitter = SkCreateRasterPipelineBlitter(fDst, p, *ctm, &innerAlloc);
501 if (dev3) {
502 handle_dev3(blitter);
503 } else {
504 handle_devVerts(blitter);
505 }
506 }
507 }
508 }
509