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