1 /*
2 * Copyright 2019 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/SkColorFilter.h"
9 #include "include/core/SkMatrix.h"
10 #include "include/core/SkRSXform.h"
11 #include "src/core/SkBlendModePriv.h"
12 #include "src/core/SkBlenderBase.h"
13 #include "src/core/SkColorSpacePriv.h"
14 #include "src/core/SkColorSpaceXformSteps.h"
15 #include "src/core/SkCoreBlitters.h"
16 #include "src/core/SkDraw.h"
17 #include "src/core/SkMatrixProvider.h"
18 #include "src/core/SkRasterClip.h"
19 #include "src/core/SkRasterPipeline.h"
20 #include "src/core/SkScan.h"
21 #include "src/core/SkVM.h"
22 #include "src/core/SkVMBlitter.h"
23 #include "src/shaders/SkShaderBase.h"
24 #include "src/shaders/SkTransformShader.h"
25
fill_rect(const SkMatrix & ctm,const SkRasterClip & rc,const SkRect & r,SkBlitter * blitter,SkPath * scratchPath)26 static void fill_rect(const SkMatrix& ctm, const SkRasterClip& rc,
27 const SkRect& r, SkBlitter* blitter, SkPath* scratchPath) {
28 if (ctm.rectStaysRect()) {
29 SkRect dr;
30 ctm.mapRect(&dr, r);
31 SkScan::FillRect(dr, rc, blitter);
32 } else {
33 SkPoint pts[4];
34 r.toQuad(pts);
35 ctm.mapPoints(pts, pts, 4);
36
37 scratchPath->rewind();
38 scratchPath->addPoly(pts, 4, true);
39 SkScan::FillPath(*scratchPath, rc, blitter);
40 }
41 }
42
load_color(SkRasterPipeline_UniformColorCtx * ctx,const float rgba[])43 static void load_color(SkRasterPipeline_UniformColorCtx* ctx, const float rgba[]) {
44 // only need one of these. can I query the pipeline to know if its lowp or highp?
45 ctx->rgba[0] = SkScalarRoundToInt(rgba[0]*255); ctx->r = rgba[0];
46 ctx->rgba[1] = SkScalarRoundToInt(rgba[1]*255); ctx->g = rgba[1];
47 ctx->rgba[2] = SkScalarRoundToInt(rgba[2]*255); ctx->b = rgba[2];
48 ctx->rgba[3] = SkScalarRoundToInt(rgba[3]*255); ctx->a = rgba[3];
49 }
50
51 extern bool gUseSkVMBlitter;
52
53 class UpdatableColorShader : public SkShaderBase {
54 public:
UpdatableColorShader(SkColorSpace * cs)55 explicit UpdatableColorShader(SkColorSpace* cs)
56 : fSteps{sk_srgb_singleton(), kUnpremul_SkAlphaType, cs, kUnpremul_SkAlphaType} {}
program(skvm::Builder * builder,skvm::Coord device,skvm::Coord local,skvm::Color paint,const MatrixRec &,const SkColorInfo & dst,skvm::Uniforms * uniforms,SkArenaAlloc * alloc) const57 skvm::Color program(skvm::Builder* builder,
58 skvm::Coord device,
59 skvm::Coord local,
60 skvm::Color paint,
61 const MatrixRec&,
62 const SkColorInfo& dst,
63 skvm::Uniforms* uniforms,
64 SkArenaAlloc* alloc) const override {
65 skvm::Uniform color = uniforms->pushPtr(fValues);
66 skvm::F32 r = builder->arrayF(color, 0);
67 skvm::F32 g = builder->arrayF(color, 1);
68 skvm::F32 b = builder->arrayF(color, 2);
69 skvm::F32 a = builder->arrayF(color, 3);
70
71 return {r, g, b, a};
72 }
73
updateColor(SkColor c) const74 void updateColor(SkColor c) const {
75 SkColor4f c4 = SkColor4f::FromColor(c);
76 fSteps.apply(c4.vec());
77 auto cp4 = c4.premul();
78 fValues[0] = cp4.fR;
79 fValues[1] = cp4.fG;
80 fValues[2] = cp4.fB;
81 fValues[3] = cp4.fA;
82 }
83
84 private:
85 // For serialization. This will never be called.
getFactory() const86 Factory getFactory() const override { return nullptr; }
getTypeName() const87 const char* getTypeName() const override { return nullptr; }
88
89 SkColorSpaceXformSteps fSteps;
90 mutable float fValues[4];
91 };
92
drawAtlas(const SkRSXform xform[],const SkRect textures[],const SkColor colors[],int count,sk_sp<SkBlender> blender,const SkPaint & paint)93 void SkDraw::drawAtlas(const SkRSXform xform[],
94 const SkRect textures[],
95 const SkColor colors[],
96 int count,
97 sk_sp<SkBlender> blender,
98 const SkPaint& paint) {
99 sk_sp<SkShader> atlasShader = paint.refShader();
100 if (!atlasShader) {
101 return;
102 }
103
104 SkSTArenaAlloc<256> alloc;
105
106 SkPaint p(paint);
107 p.setAntiAlias(false); // we never respect this for drawAtlas(or drawVertices)
108 p.setStyle(SkPaint::kFill_Style);
109 p.setShader(nullptr);
110 p.setMaskFilter(nullptr);
111
112 const SkMatrix& ctm = fMatrixProvider->localToDevice();
113 // The RSXForms can't contain perspective - only the CTM cab.
114 const bool perspective = ctm.hasPerspective();
115
116 auto transformShader = alloc.make<SkTransformShader>(*as_SB(atlasShader), perspective);
117
118 auto rpblit = [&]() {
119 SkRasterPipeline pipeline(&alloc);
120 SkSurfaceProps props = SkSurfacePropsCopyOrDefault(fProps);
121 SkStageRec rec = {&pipeline, &alloc, fDst.colorType(), fDst.colorSpace(), p, props};
122 // We pass an identity matrix here rather than the CTM. The CTM gets folded into the
123 // per-triangle matrix.
124 if (!as_SB(transformShader)->appendRootStages(rec, SkMatrix::I())) {
125 return false;
126 }
127
128 SkRasterPipeline_UniformColorCtx* uniformCtx = nullptr;
129 SkColorSpaceXformSteps steps(
130 sk_srgb_singleton(), kUnpremul_SkAlphaType, rec.fDstCS, kUnpremul_SkAlphaType);
131
132 if (colors) {
133 // we will late-bind the values in ctx, once for each color in the loop
134 uniformCtx = alloc.make<SkRasterPipeline_UniformColorCtx>();
135 rec.fPipeline->append(SkRasterPipelineOp::uniform_color_dst, uniformCtx);
136 if (std::optional<SkBlendMode> bm = as_BB(blender)->asBlendMode(); bm.has_value()) {
137 SkBlendMode_AppendStages(*bm, rec.fPipeline);
138 } else {
139 return false;
140 }
141 }
142
143 bool isOpaque = !colors && transformShader->isOpaque();
144 if (p.getAlphaf() != 1) {
145 rec.fPipeline->append(SkRasterPipelineOp::scale_1_float,
146 alloc.make<float>(p.getAlphaf()));
147 isOpaque = false;
148 }
149
150 auto blitter = SkCreateRasterPipelineBlitter(
151 fDst, p, pipeline, isOpaque, &alloc, fRC->clipShader());
152 if (!blitter) {
153 return false;
154 }
155 SkPath scratchPath;
156
157 for (int i = 0; i < count; ++i) {
158 if (colors) {
159 SkColor4f c4 = SkColor4f::FromColor(colors[i]);
160 steps.apply(c4.vec());
161 load_color(uniformCtx, c4.premul().vec());
162 }
163
164 SkMatrix mx;
165 mx.setRSXform(xform[i]);
166 mx.preTranslate(-textures[i].fLeft, -textures[i].fTop);
167 mx.postConcat(ctm);
168 if (transformShader->update(mx)) {
169 fill_rect(mx, *fRC, textures[i], blitter, &scratchPath);
170 }
171 }
172 return true;
173 };
174
175 if (gUseSkVMBlitter || !rpblit()) {
176 UpdatableColorShader* colorShader = nullptr;
177 sk_sp<SkShader> shader;
178 if (colors) {
179 colorShader = alloc.make<UpdatableColorShader>(fDst.colorSpace());
180 shader = SkShaders::Blend(std::move(blender),
181 sk_ref_sp(colorShader),
182 sk_ref_sp(transformShader));
183 } else {
184 shader = sk_ref_sp(transformShader);
185 }
186 p.setShader(std::move(shader));
187 // We use identity here and fold the CTM into the update matrix.
188 if (auto blitter = SkVMBlitter::Make(fDst,
189 p,
190 SkMatrix::I(),
191 &alloc,
192 fRC->clipShader())) {
193 SkPath scratchPath;
194 for (int i = 0; i < count; ++i) {
195 if (colorShader) {
196 colorShader->updateColor(colors[i]);
197 }
198
199 SkMatrix mx;
200 mx.setRSXform(xform[i]);
201 mx.preTranslate(-textures[i].fLeft, -textures[i].fTop);
202 mx.postConcat(ctm);
203 if (transformShader->update(mx)) {
204 fill_rect(mx, *fRC, textures[i], blitter, &scratchPath);
205 }
206 }
207 }
208 }
209 }
210