• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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