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 | // |
27 | static 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 | |
34 | static 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). |
40 | static 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 | |
47 | constexpr 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. |
51 | static 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 | |
80 | struct 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 | |
107 | private: |
108 | float dot(int index, float x, float y) const { |
109 | return fMat[index + 0] * x + fMat[index + 4] * y; |
110 | } |
111 | }; |
112 | |
113 | static bool SK_WARN_UNUSED_RESULT |
114 | texture_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 | |
127 | class SkTriColorShader : public SkShaderBase { |
128 | public: |
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 | |
135 | protected: |
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 | |
150 | private: |
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 | |
167 | bool 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 | // |
206 | static 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 | |
217 | static 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 | |
225 | static 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 | |
233 | static 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 | } |
248 | static 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 | |
257 | void 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 | |
410 | void 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 | |
417 | void 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 | |