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