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