1 /*
2 * Copyright 2015 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/SkAlphaType.h"
9 #include "include/core/SkBitmap.h"
10 #include "include/core/SkColorFilter.h"
11 #include "include/core/SkFlattenable.h"
12 #include "include/core/SkImageInfo.h"
13 #include "include/core/SkRefCnt.h"
14 #include "include/core/SkString.h"
15 #include "include/core/SkTypes.h"
16 #include "include/private/SkSLSampleUsage.h"
17 #include "src/base/SkArenaAlloc.h"
18 #include "src/core/SkColorFilterBase.h"
19 #include "src/core/SkEffectPriv.h"
20 #include "src/core/SkRasterPipeline.h"
21 #include "src/core/SkRasterPipelineOpContexts.h"
22 #include "src/core/SkRasterPipelineOpList.h"
23 #include "src/core/SkReadBuffer.h"
24 #include "src/core/SkWriteBuffer.h"
25
26 #include <cstdint>
27 #include <memory>
28 #include <tuple>
29 #include <utility>
30
31 #if defined(SK_GRAPHITE)
32 #include "src/gpu/graphite/Image_Graphite.h"
33 #include "src/gpu/graphite/KeyContext.h"
34 #include "src/gpu/graphite/KeyHelpers.h"
35 #include "src/gpu/graphite/PaintParamsKey.h"
36
37 namespace skgpu::graphite {
38 class PipelineDataGatherer;
39 }
40 #endif
41
42 #if defined(SK_GANESH)
43 #include "include/gpu/GpuTypes.h"
44 #include "include/gpu/GrTypes.h"
45 #include "src/gpu/ganesh/GrColorInfo.h"
46 #include "src/gpu/ganesh/GrFragmentProcessor.h"
47 #include "src/gpu/ganesh/GrProcessorUnitTest.h"
48 #include "src/gpu/ganesh/GrSurfaceProxyView.h"
49 #include "src/gpu/ganesh/SkGr.h"
50 #include "src/gpu/ganesh/effects/GrTextureEffect.h"
51 #include "src/gpu/ganesh/glsl/GrGLSLFragmentShaderBuilder.h"
52
53 class GrRecordingContext;
54 struct GrShaderCaps;
55 namespace skgpu { class KeyBuilder; }
56 #endif
57
58 #if GR_TEST_UTILS
59 #include "include/core/SkColorSpace.h"
60 #include "include/core/SkSurfaceProps.h"
61 #include "include/private/base/SkTo.h"
62 #include "include/private/gpu/ganesh/GrTypesPriv.h"
63 #include "src/base/SkRandom.h"
64 #include "src/gpu/ganesh/GrTestUtils.h"
65 #else
66 class SkSurfaceProps;
67 #endif
68
69 #if defined(SK_ENABLE_SKSL)
70 #include "src/core/SkVM.h"
71 #endif
72
73 class SkTable_ColorFilter final : public SkColorFilterBase {
74 public:
SkTable_ColorFilter(const uint8_t tableA[],const uint8_t tableR[],const uint8_t tableG[],const uint8_t tableB[])75 SkTable_ColorFilter(const uint8_t tableA[], const uint8_t tableR[],
76 const uint8_t tableG[], const uint8_t tableB[]) {
77 fBitmap.allocPixels(SkImageInfo::MakeA8(256, 4));
78 uint8_t *a = fBitmap.getAddr8(0,0),
79 *r = fBitmap.getAddr8(0,1),
80 *g = fBitmap.getAddr8(0,2),
81 *b = fBitmap.getAddr8(0,3);
82 for (int i = 0; i < 256; i++) {
83 a[i] = tableA ? tableA[i] : i;
84 r[i] = tableR ? tableR[i] : i;
85 g[i] = tableG ? tableG[i] : i;
86 b[i] = tableB ? tableB[i] : i;
87 }
88 fBitmap.setImmutable();
89 }
90
91 #if defined(SK_GANESH)
92 GrFPResult asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,
93 GrRecordingContext*, const GrColorInfo&,
94 const SkSurfaceProps&) const override;
95 #endif
96
97 #if defined(SK_GRAPHITE)
98 void addToKey(const skgpu::graphite::KeyContext&,
99 skgpu::graphite::PaintParamsKeyBuilder*,
100 skgpu::graphite::PipelineDataGatherer*) const override;
101 #endif
102
appendStages(const SkStageRec & rec,bool shaderIsOpaque) const103 bool appendStages(const SkStageRec& rec, bool shaderIsOpaque) const override {
104 SkRasterPipeline* p = rec.fPipeline;
105 if (!shaderIsOpaque) {
106 p->append(SkRasterPipelineOp::unpremul);
107 }
108
109 SkRasterPipeline_TablesCtx* tables = rec.fAlloc->make<SkRasterPipeline_TablesCtx>();
110 tables->a = fBitmap.getAddr8(0, 0);
111 tables->r = fBitmap.getAddr8(0, 1);
112 tables->g = fBitmap.getAddr8(0, 2);
113 tables->b = fBitmap.getAddr8(0, 3);
114 p->append(SkRasterPipelineOp::byte_tables, tables);
115
116 bool definitelyOpaque = shaderIsOpaque && tables->a[0xff] == 0xff;
117 if (!definitelyOpaque) {
118 p->append(SkRasterPipelineOp::premul);
119 }
120 return true;
121 }
122
onProgram(skvm::Builder * p,skvm::Color c,const SkColorInfo & dst,skvm::Uniforms * uniforms,SkArenaAlloc *) const123 skvm::Color onProgram(skvm::Builder* p, skvm::Color c,
124 const SkColorInfo& dst,
125 skvm::Uniforms* uniforms, SkArenaAlloc*) const override {
126
127 auto apply_table_to_component = [&](skvm::F32 c, const uint8_t* bytePtr) -> skvm::F32 {
128 skvm::I32 index = to_unorm(8, clamp01(c));
129 skvm::Uniform table = uniforms->pushPtr(bytePtr);
130 return from_unorm(8, gather8(table, index));
131 };
132
133 c = unpremul(c);
134 c.a = apply_table_to_component(c.a, fBitmap.getAddr8(0,0));
135 c.r = apply_table_to_component(c.r, fBitmap.getAddr8(0,1));
136 c.g = apply_table_to_component(c.g, fBitmap.getAddr8(0,2));
137 c.b = apply_table_to_component(c.b, fBitmap.getAddr8(0,3));
138 return premul(c);
139 }
140
flatten(SkWriteBuffer & buffer) const141 void flatten(SkWriteBuffer& buffer) const override {
142 buffer.writeByteArray(fBitmap.getAddr8(0,0), 4*256);
143 }
144
145 private:
146 friend void ::SkRegisterTableColorFilterFlattenable();
147 SK_FLATTENABLE_HOOKS(SkTable_ColorFilter)
148
149 SkBitmap fBitmap;
150 };
151
CreateProc(SkReadBuffer & buffer)152 sk_sp<SkFlattenable> SkTable_ColorFilter::CreateProc(SkReadBuffer& buffer) {
153 uint8_t argb[4*256];
154 if (buffer.readByteArray(argb, sizeof(argb))) {
155 return SkColorFilters::TableARGB(argb+0*256, argb+1*256, argb+2*256, argb+3*256);
156 }
157 return nullptr;
158 }
159
160 #if defined(SK_GANESH)
161
162 class ColorTableEffect : public GrFragmentProcessor {
163 public:
164 static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> inputFP,
165 GrRecordingContext* context,
166 const SkBitmap& bitmap);
167
~ColorTableEffect()168 ~ColorTableEffect() override {}
169
name() const170 const char* name() const override { return "ColorTableEffect"; }
171
clone() const172 std::unique_ptr<GrFragmentProcessor> clone() const override {
173 return std::unique_ptr<GrFragmentProcessor>(new ColorTableEffect(*this));
174 }
175
176 inline static constexpr int kTexEffectFPIndex = 0;
177 inline static constexpr int kInputFPIndex = 1;
178
179 private:
180 std::unique_ptr<ProgramImpl> onMakeProgramImpl() const override;
181
onAddToKey(const GrShaderCaps &,skgpu::KeyBuilder *) const182 void onAddToKey(const GrShaderCaps&, skgpu::KeyBuilder*) const override {}
183
onIsEqual(const GrFragmentProcessor &) const184 bool onIsEqual(const GrFragmentProcessor&) const override { return true; }
185
186 ColorTableEffect(std::unique_ptr<GrFragmentProcessor> inputFP, GrSurfaceProxyView view);
187
188 explicit ColorTableEffect(const ColorTableEffect& that);
189
190 GR_DECLARE_FRAGMENT_PROCESSOR_TEST
191
192 using INHERITED = GrFragmentProcessor;
193 };
194
ColorTableEffect(std::unique_ptr<GrFragmentProcessor> inputFP,GrSurfaceProxyView view)195 ColorTableEffect::ColorTableEffect(std::unique_ptr<GrFragmentProcessor> inputFP,
196 GrSurfaceProxyView view)
197 // Not bothering with table-specific optimizations.
198 : INHERITED(kColorTableEffect_ClassID, kNone_OptimizationFlags) {
199 this->registerChild(GrTextureEffect::Make(std::move(view), kUnknown_SkAlphaType),
200 SkSL::SampleUsage::Explicit());
201 this->registerChild(std::move(inputFP));
202 }
203
ColorTableEffect(const ColorTableEffect & that)204 ColorTableEffect::ColorTableEffect(const ColorTableEffect& that)
205 : INHERITED(that) {}
206
onMakeProgramImpl() const207 std::unique_ptr<GrFragmentProcessor::ProgramImpl> ColorTableEffect::onMakeProgramImpl() const {
208 class Impl : public ProgramImpl {
209 public:
210 void emitCode(EmitArgs& args) override {
211 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
212 SkString inputColor = this->invokeChild(kInputFPIndex, args);
213 SkString a = this->invokeChild(kTexEffectFPIndex, args, "half2(coord.a, 0.5)");
214 SkString r = this->invokeChild(kTexEffectFPIndex, args, "half2(coord.r, 1.5)");
215 SkString g = this->invokeChild(kTexEffectFPIndex, args, "half2(coord.g, 2.5)");
216 SkString b = this->invokeChild(kTexEffectFPIndex, args, "half2(coord.b, 3.5)");
217 fragBuilder->codeAppendf(
218 "half4 coord = 255 * unpremul(%s) + 0.5;\n"
219 "half4 color = half4(%s.a, %s.a, %s.a, 1);\n"
220 "return color * %s.a;\n",
221 inputColor.c_str(), r.c_str(), g.c_str(), b.c_str(), a.c_str());
222 }
223 };
224
225 return std::make_unique<Impl>();
226 }
227
Make(std::unique_ptr<GrFragmentProcessor> inputFP,GrRecordingContext * context,const SkBitmap & bitmap)228 std::unique_ptr<GrFragmentProcessor> ColorTableEffect::Make(
229 std::unique_ptr<GrFragmentProcessor> inputFP,
230 GrRecordingContext* context, const SkBitmap& bitmap) {
231 SkASSERT(kPremul_SkAlphaType == bitmap.alphaType());
232 SkASSERT(bitmap.isImmutable());
233
234 auto view = std::get<0>(GrMakeCachedBitmapProxyView(context,
235 bitmap,
236 /*label=*/"MakeColorTableEffect",
237 GrMipmapped::kNo));
238 if (!view) {
239 return nullptr;
240 }
241
242 return std::unique_ptr<GrFragmentProcessor>(new ColorTableEffect(std::move(inputFP),
243 std::move(view)));
244 }
245
246 ///////////////////////////////////////////////////////////////////////////////
247
GR_DEFINE_FRAGMENT_PROCESSOR_TEST(ColorTableEffect)248 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(ColorTableEffect)
249
250 #if GR_TEST_UTILS
251
252
253 std::unique_ptr<GrFragmentProcessor> ColorTableEffect::TestCreate(GrProcessorTestData* d) {
254 int flags = 0;
255 uint8_t luts[256][4];
256 do {
257 for (int i = 0; i < 4; ++i) {
258 flags |= d->fRandom->nextBool() ? (1 << i): 0;
259 }
260 } while (!flags);
261 for (int i = 0; i < 4; ++i) {
262 if (flags & (1 << i)) {
263 for (int j = 0; j < 256; ++j) {
264 luts[j][i] = SkToU8(d->fRandom->nextBits(8));
265 }
266 }
267 }
268 auto filter(SkColorFilters::TableARGB(
269 (flags & (1 << 0)) ? luts[0] : nullptr,
270 (flags & (1 << 1)) ? luts[1] : nullptr,
271 (flags & (1 << 2)) ? luts[2] : nullptr,
272 (flags & (1 << 3)) ? luts[3] : nullptr
273 ));
274 sk_sp<SkColorSpace> colorSpace = GrTest::TestColorSpace(d->fRandom);
275 SkSurfaceProps props; // default props for testing
276 auto [success, fp] = as_CFB(filter)->asFragmentProcessor(
277 d->inputFP(), d->context(),
278 GrColorInfo(GrColorType::kRGBA_8888, kUnknown_SkAlphaType, std::move(colorSpace)),
279 props);
280 SkASSERT(success);
281 return std::move(fp);
282 }
283 #endif
284
asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,GrRecordingContext * context,const GrColorInfo &,const SkSurfaceProps &) const285 GrFPResult SkTable_ColorFilter::asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,
286 GrRecordingContext* context,
287 const GrColorInfo&,
288 const SkSurfaceProps&) const {
289 auto cte = ColorTableEffect::Make(std::move(inputFP), context, fBitmap);
290 return cte ? GrFPSuccess(std::move(cte)) : GrFPFailure(nullptr);
291 }
292
293 #endif // defined(SK_GANESH)
294
295 #if defined(SK_GRAPHITE)
296
addToKey(const skgpu::graphite::KeyContext & keyContext,skgpu::graphite::PaintParamsKeyBuilder * builder,skgpu::graphite::PipelineDataGatherer * gatherer) const297 void SkTable_ColorFilter::addToKey(const skgpu::graphite::KeyContext& keyContext,
298 skgpu::graphite::PaintParamsKeyBuilder* builder,
299 skgpu::graphite::PipelineDataGatherer* gatherer) const {
300 using namespace skgpu::graphite;
301
302 TableColorFilterBlock::TableColorFilterData data;
303
304 // TODO(b/239604347): remove this hack. This is just here until we determine what Graphite's
305 // Recorder-level caching story is going to be.
306 sk_sp<SkImage> image = SkImage::MakeFromBitmap(fBitmap);
307 image = image->makeTextureImage(keyContext.recorder(), { skgpu::Mipmapped::kNo });
308
309 if (as_IB(image)->isGraphiteBacked()) {
310 skgpu::graphite::Image* grImage = static_cast<skgpu::graphite::Image*>(image.get());
311
312 auto [view, _] = grImage->asView(keyContext.recorder(), skgpu::Mipmapped::kNo);
313 data.fTextureProxy = view.refProxy();
314 }
315
316 TableColorFilterBlock::BeginBlock(keyContext, builder, gatherer, data);
317 builder->endBlock();
318 }
319
320 #endif
321
322 ///////////////////////////////////////////////////////////////////////////////
323
Table(const uint8_t table[256])324 sk_sp<SkColorFilter> SkColorFilters::Table(const uint8_t table[256]) {
325 return sk_make_sp<SkTable_ColorFilter>(table, table, table, table);
326 }
327
TableARGB(const uint8_t tableA[256],const uint8_t tableR[256],const uint8_t tableG[256],const uint8_t tableB[256])328 sk_sp<SkColorFilter> SkColorFilters::TableARGB(const uint8_t tableA[256],
329 const uint8_t tableR[256],
330 const uint8_t tableG[256],
331 const uint8_t tableB[256]) {
332 if (!tableA && !tableR && !tableG && !tableB) {
333 return nullptr;
334 }
335
336 return sk_make_sp<SkTable_ColorFilter>(tableA, tableR, tableG, tableB);
337 }
338
SkRegisterTableColorFilterFlattenable()339 void SkRegisterTableColorFilterFlattenable() {
340 SK_REGISTER_FLATTENABLE(SkTable_ColorFilter);
341 }
342