• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 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 "src/core/SkMatrixProvider.h"
9 #include "src/core/SkRasterPipeline.h"
10 #include "src/shaders/SkTransformShader.h"
11 
SkTransformShader(const SkShaderBase & shader,bool allowPerspective)12 SkTransformShader::SkTransformShader(const SkShaderBase& shader, bool allowPerspective)
13         : fShader{shader}, fAllowPerspective{allowPerspective} {
14     SkMatrix::I().get9(fMatrixStorage);
15 }
16 
program(skvm::Builder * b,skvm::Coord device,skvm::Coord local,skvm::Color color,const MatrixRec & mRec,const SkColorInfo & dst,skvm::Uniforms * uniforms,SkArenaAlloc * alloc) const17 skvm::Color SkTransformShader::program(skvm::Builder* b,
18                                        skvm::Coord device,
19                                        skvm::Coord local,
20                                        skvm::Color color,
21                                        const MatrixRec& mRec,
22                                        const SkColorInfo& dst,
23                                        skvm::Uniforms* uniforms,
24                                        SkArenaAlloc* alloc) const {
25     // We have to seed and apply any constant matrices before appending our matrix that may
26     // mutate. We could try to apply one matrix stage and then incorporate the parent matrix
27     // with the variable matrix in each call to update(). However, in practice our callers
28     // fold the CTM into the update() matrix and don't wrap the transform shader in local matrix
29     // shaders so the call to apply below should be no-op. If this assert fires it just indicates an
30     // optimization opportunity, not a correctness bug.
31     SkASSERT(!mRec.hasPendingMatrix());
32 
33     std::optional<MatrixRec> childMRec = mRec.apply(b, &local, uniforms);
34     if (!childMRec.has_value()) {
35         return {};
36     }
37     // The matrix we're about to insert gets updated between uses of the VM so our children can't
38     // know the total transform when they add their stages. We don't incorporate this shader's
39     // matrix into the MatrixRec at all.
40     childMRec->markTotalMatrixInvalid();
41 
42     auto matrix = uniforms->pushPtr(&fMatrixStorage);
43 
44     skvm::F32 x = local.x,
45               y = local.y;
46 
47     auto dot = [&, x, y](int row) {
48         return b->mad(x,
49                       b->arrayF(matrix, 3 * row + 0),
50                       b->mad(y, b->arrayF(matrix, 3 * row + 1), b->arrayF(matrix, 3 * row + 2)));
51     };
52 
53     x = dot(0);
54     y = dot(1);
55     if (fAllowPerspective) {
56         x = x * (1.0f / dot(2));
57         y = y * (1.0f / dot(2));
58     }
59 
60     skvm::Coord newLocal = {x, y};
61     return fShader.program(b, device, newLocal, color, *childMRec, dst, uniforms, alloc);
62 }
63 
update(const SkMatrix & matrix)64 bool SkTransformShader::update(const SkMatrix& matrix) {
65     if (SkMatrix inv; matrix.invert(&inv)) {
66         if (!fAllowPerspective && inv.hasPerspective()) {
67             return false;
68         }
69 
70         inv.get9(fMatrixStorage);
71         return true;
72     }
73     return false;
74 }
75 
appendStages(const SkStageRec & rec,const MatrixRec & mRec) const76 bool SkTransformShader::appendStages(const SkStageRec& rec, const MatrixRec& mRec) const {
77     // We have to seed and apply any constant matrices before appending our matrix that may
78     // mutate. We could try to add one matrix stage and then incorporate the parent matrix
79     // with the variable matrix in each call to update(). However, in practice our callers
80     // fold the CTM into the update() matrix and don't wrap the transform shader in local matrix
81     // shaders so the call to apply below should just seed the coordinates. If this assert fires
82     // it just indicates an optimization opportunity, not a correctness bug.
83     SkASSERT(!mRec.hasPendingMatrix());
84     std::optional<MatrixRec> childMRec = mRec.apply(rec);
85     if (!childMRec.has_value()) {
86         return false;
87     }
88     // The matrix we're about to insert gets updated between uses of the pipeline so our children
89     // can't know the total transform when they add their stages. We don't even incorporate this
90     // matrix into the MatrixRec at all.
91     childMRec->markTotalMatrixInvalid();
92 
93     auto type = fAllowPerspective ? SkRasterPipelineOp::matrix_perspective
94                                   : SkRasterPipelineOp::matrix_2x3;
95     rec.fPipeline->append(type, fMatrixStorage);
96 
97     fShader.appendStages(rec, *childMRec);
98     return true;
99 }
100