• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2023 The Android Open Source Project
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/SkFlattenable.h"
9 #include "src/base/SkArenaAlloc.h"
10 #include "src/core/SkRasterPipeline.h"
11 #include "src/core/SkReadBuffer.h"
12 #include "src/core/SkRuntimeEffectPriv.h"
13 #include "src/core/SkVM.h"
14 #include "src/core/SkWriteBuffer.h"
15 #include "src/shaders/SkShaderBase.h"
16 
17 #if defined(SK_GANESH)
18 #include "include/effects/SkRuntimeEffect.h"
19 #include "include/gpu/GrRecordingContext.h"
20 #include "src/gpu/ganesh/GrFPArgs.h"
21 #include "src/gpu/ganesh/GrFragmentProcessor.h"
22 #include "src/gpu/ganesh/effects/GrMatrixEffect.h"
23 #include "src/gpu/ganesh/effects/GrSkSLFP.h"
24 #endif
25 
26 class SkShader_CoordClamp final : public SkShaderBase {
27 public:
SkShader_CoordClamp(sk_sp<SkShader> shader,const SkRect & subset)28     SkShader_CoordClamp(sk_sp<SkShader> shader, const SkRect& subset)
29             : fShader(std::move(shader)), fSubset(subset) {}
30 
31 #if defined(SK_GANESH)
32     std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs&,
33                                                              const MatrixRec&) const override;
34 #endif
35 
36 protected:
37     SkShader_CoordClamp(SkReadBuffer&);
38     void flatten(SkWriteBuffer&) const override;
39     bool appendStages(const SkStageRec&, const MatrixRec&) const override;
40     skvm::Color program(skvm::Builder*,
41                         skvm::Coord device,
42                         skvm::Coord local,
43                         skvm::Color paint,
44                         const MatrixRec&,
45                         const SkColorInfo& dst,
46                         skvm::Uniforms*,
47                         SkArenaAlloc*) const override;
48 
49 private:
50     friend void ::SkRegisterCoordClampShaderFlattenable();
51     SK_FLATTENABLE_HOOKS(SkShader_CoordClamp)
52 
53     sk_sp<SkShader> fShader;
54     SkRect fSubset;
55 };
56 
CreateProc(SkReadBuffer & buffer)57 sk_sp<SkFlattenable> SkShader_CoordClamp::CreateProc(SkReadBuffer& buffer) {
58     sk_sp<SkShader> shader(buffer.readShader());
59     SkRect subset = buffer.readRect();
60     if (!buffer.validate(SkToBool(shader))) {
61         return nullptr;
62     }
63     return SkShaders::CoordClamp(std::move(shader), subset);
64 }
65 
flatten(SkWriteBuffer & buffer) const66 void SkShader_CoordClamp::flatten(SkWriteBuffer& buffer) const {
67     buffer.writeFlattenable(fShader.get());
68     buffer.writeRect(fSubset);
69 }
70 
appendStages(const SkStageRec & rec,const MatrixRec & mRec) const71 bool SkShader_CoordClamp::appendStages(const SkStageRec& rec, const MatrixRec& mRec) const {
72     std::optional<MatrixRec> childMRec = mRec.apply(rec);
73     if (!childMRec.has_value()) {
74         return false;
75     }
76     // Strictly speaking, childMRec's total matrix is not valid. It is only valid inside the subset
77     // rectangle. However, we don't mark it as such because we want the "total matrix is valid"
78     // behavior in SkImageShader for filtering.
79     auto clampCtx = rec.fAlloc->make<SkRasterPipeline_CoordClampCtx>();
80     *clampCtx = {fSubset.fLeft, fSubset.fTop, fSubset.fRight, fSubset.fBottom};
81     rec.fPipeline->append(SkRasterPipelineOp::clamp_x_and_y, clampCtx);
82     return as_SB(fShader)->appendStages(rec, *childMRec);
83 }
84 
program(skvm::Builder * p,skvm::Coord device,skvm::Coord local,skvm::Color paint,const MatrixRec & mRec,const SkColorInfo & cinfo,skvm::Uniforms * uniforms,SkArenaAlloc * alloc) const85 skvm::Color SkShader_CoordClamp::program(skvm::Builder* p,
86                                          skvm::Coord device,
87                                          skvm::Coord local,
88                                          skvm::Color paint,
89                                          const MatrixRec& mRec,
90                                          const SkColorInfo& cinfo,
91                                          skvm::Uniforms* uniforms,
92                                          SkArenaAlloc* alloc) const {
93     std::optional<MatrixRec> childMRec = mRec.apply(p, &local, uniforms);
94     if (!childMRec.has_value()) {
95         return {};
96     }
97     // See comment in appendStages about not marking childMRec with an invalid total matrix.
98 
99     auto l = uniforms->pushF(fSubset.left());
100     auto t = uniforms->pushF(fSubset.top());
101     auto r = uniforms->pushF(fSubset.right());
102     auto b = uniforms->pushF(fSubset.bottom());
103 
104     local.x = p->clamp(local.x, p->uniformF(l), p->uniformF(r));
105     local.y = p->clamp(local.y, p->uniformF(t), p->uniformF(b));
106 
107     return as_SB(fShader)->program(p, device, local, paint, *childMRec, cinfo, uniforms, alloc);
108 }
109 
110 #if defined(SK_GANESH)
asFragmentProcessor(const GrFPArgs & args,const MatrixRec & mRec) const111 std::unique_ptr<GrFragmentProcessor> SkShader_CoordClamp::asFragmentProcessor(
112         const GrFPArgs& args, const MatrixRec& mRec) const {
113     static const SkRuntimeEffect* effect = SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader,
114             "uniform shader c;"
115             "uniform float4 s;"
116             "half4 main(float2 p) {"
117                 "return c.eval(clamp(p, s.LT, s.RB));"
118             "}");
119 
120     auto fp = as_SB(fShader)->asFragmentProcessor(args, mRec.applied());
121     if (!fp) {
122         return nullptr;
123     }
124 
125     GrSkSLFP::OptFlags flags = GrSkSLFP::OptFlags::kNone;
126     if (fp->compatibleWithCoverageAsAlpha()) {
127         flags |= GrSkSLFP::OptFlags::kCompatibleWithCoverageAsAlpha;
128     }
129     if (fp->preservesOpaqueInput()) {
130         flags |= GrSkSLFP::OptFlags::kPreservesOpaqueInput;
131     }
132     fp = GrSkSLFP::Make(effect,
133                         "clamp_fp",
134                         /*inputFP=*/nullptr,
135                         flags,
136                         "c", std::move(fp),
137                         "s", fSubset);
138     bool success;
139     std::tie(success, fp) = mRec.apply(std::move(fp));
140     return success ? std::move(fp) : nullptr;
141 }
142 #endif  // defined(SK_GANESH)
143 
SkRegisterCoordClampShaderFlattenable()144 void SkRegisterCoordClampShaderFlattenable() { SK_REGISTER_FLATTENABLE(SkShader_CoordClamp); }
145 
CoordClamp(sk_sp<SkShader> shader,const SkRect & subset)146 sk_sp<SkShader> SkShaders::CoordClamp(sk_sp<SkShader> shader, const SkRect& subset) {
147     if (!shader) {
148         return nullptr;
149     }
150     if (!subset.isSorted()) {
151         return nullptr;
152     }
153     return sk_make_sp<SkShader_CoordClamp>(std::move(shader), subset);
154 }
155