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/effects/SkTableColorFilter.h" |
9 | |
10 | #include "include/core/SkBitmap.h" |
11 | #include "include/core/SkString.h" |
12 | #include "include/core/SkUnPreMultiply.h" |
13 | #include "include/private/SkColorData.h" |
14 | #include "include/private/SkTo.h" |
15 | #include "src/core/SkArenaAlloc.h" |
16 | #include "src/core/SkEffectPriv.h" |
17 | #include "src/core/SkRasterPipeline.h" |
18 | #include "src/core/SkReadBuffer.h" |
19 | #include "src/core/SkVM.h" |
20 | #include "src/core/SkWriteBuffer.h" |
21 | |
22 | static const uint8_t gIdentityTable[] = { |
23 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, |
24 | 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, |
25 | 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, |
26 | 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, |
27 | 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, |
28 | 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, |
29 | 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, |
30 | 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, |
31 | 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, |
32 | 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, |
33 | 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, |
34 | 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, |
35 | 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, |
36 | 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, |
37 | 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, |
38 | 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, |
39 | 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, |
40 | 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, |
41 | 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, |
42 | 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, |
43 | 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, |
44 | 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, |
45 | 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, |
46 | 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, |
47 | 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, |
48 | 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, |
49 | 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, |
50 | 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, |
51 | 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, |
52 | 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, |
53 | 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, |
54 | 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF |
55 | }; |
56 | |
57 | class SkTable_ColorFilter : public SkColorFilter { |
58 | public: |
59 | SkTable_ColorFilter(const uint8_t tableA[], const uint8_t tableR[], |
60 | const uint8_t tableG[], const uint8_t tableB[]) { |
61 | fBitmap = nullptr; |
62 | fFlags = 0; |
63 | |
64 | uint8_t* dst = fStorage; |
65 | if (tableA) { |
66 | memcpy(dst, tableA, 256); |
67 | dst += 256; |
68 | fFlags |= kA_Flag; |
69 | } |
70 | if (tableR) { |
71 | memcpy(dst, tableR, 256); |
72 | dst += 256; |
73 | fFlags |= kR_Flag; |
74 | } |
75 | if (tableG) { |
76 | memcpy(dst, tableG, 256); |
77 | dst += 256; |
78 | fFlags |= kG_Flag; |
79 | } |
80 | if (tableB) { |
81 | memcpy(dst, tableB, 256); |
82 | fFlags |= kB_Flag; |
83 | } |
84 | } |
85 | |
86 | ~SkTable_ColorFilter() override { delete fBitmap; } |
87 | |
88 | #if SK_SUPPORT_GPU |
89 | std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(GrRecordingContext*, |
90 | const GrColorInfo&) const override; |
91 | #endif |
92 | |
93 | enum { |
94 | kA_Flag = 1 << 0, |
95 | kR_Flag = 1 << 1, |
96 | kG_Flag = 1 << 2, |
97 | kB_Flag = 1 << 3, |
98 | }; |
99 | |
100 | bool onAppendStages(const SkStageRec& rec, bool shaderIsOpaque) const override { |
101 | const uint8_t *r = gIdentityTable, |
102 | *g = gIdentityTable, |
103 | *b = gIdentityTable, |
104 | *a = gIdentityTable; |
105 | const uint8_t* ptr = fStorage; |
106 | if (fFlags & kA_Flag) { a = ptr; ptr += 256; } |
107 | if (fFlags & kR_Flag) { r = ptr; ptr += 256; } |
108 | if (fFlags & kG_Flag) { g = ptr; ptr += 256; } |
109 | if (fFlags & kB_Flag) { b = ptr; } |
110 | |
111 | SkRasterPipeline* p = rec.fPipeline; |
112 | if (!shaderIsOpaque) { |
113 | p->append(SkRasterPipeline::unpremul); |
114 | } |
115 | |
116 | struct Tables { const uint8_t *r, *g, *b, *a; }; |
117 | p->append(SkRasterPipeline::byte_tables, rec.fAlloc->make<Tables>(Tables{r,g,b,a})); |
118 | |
119 | bool definitelyOpaque = shaderIsOpaque && a[0xff] == 0xff; |
120 | if (!definitelyOpaque) { |
121 | p->append(SkRasterPipeline::premul); |
122 | } |
123 | return true; |
124 | } |
125 | |
126 | skvm::Color onProgram(skvm::Builder* p, skvm::Color c, |
127 | SkColorSpace* dstCS, |
128 | skvm::Uniforms* uniforms, SkArenaAlloc*) const override { |
129 | |
130 | auto apply_table_to_component = [&](skvm::F32 c, const uint8_t* bytePtr) -> skvm::F32 { |
131 | skvm::I32 index = to_unorm(8, clamp01(c)); |
132 | skvm::Uniform table = uniforms->pushPtr(bytePtr); |
133 | return from_unorm(8, gather8(table, index)); |
134 | }; |
135 | |
136 | c = unpremul(c); |
137 | |
138 | const uint8_t* ptr = fStorage; |
139 | if (fFlags & kA_Flag) { |
140 | c.a = apply_table_to_component(c.a, ptr); |
141 | ptr += 256; |
142 | } |
143 | if (fFlags & kR_Flag) { |
144 | c.r = apply_table_to_component(c.r, ptr); |
145 | ptr += 256; |
146 | } |
147 | if (fFlags & kG_Flag) { |
148 | c.g = apply_table_to_component(c.g, ptr); |
149 | ptr += 256; |
150 | } |
151 | if (fFlags & kB_Flag) { |
152 | c.b = apply_table_to_component(c.b, ptr); |
153 | } |
154 | return premul(c); |
155 | } |
156 | |
157 | protected: |
158 | void flatten(SkWriteBuffer&) const override; |
159 | |
160 | private: |
161 | SK_FLATTENABLE_HOOKS(SkTable_ColorFilter) |
162 | |
163 | void getTableAsBitmap(SkBitmap* table) const; |
164 | |
165 | mutable const SkBitmap* fBitmap; // lazily allocated |
166 | |
167 | uint8_t fStorage[256 * 4]; |
168 | unsigned fFlags; |
169 | |
170 | friend class SkTableColorFilter; |
171 | |
172 | typedef SkColorFilter INHERITED; |
173 | }; |
174 | |
175 | static const uint8_t gCountNibBits[] = { |
176 | 0, 1, 1, 2, |
177 | 1, 2, 2, 3, |
178 | 1, 2, 2, 3, |
179 | 2, 3, 3, 4 |
180 | }; |
181 | |
182 | #include "src/effects/SkPackBits.h" |
183 | |
184 | void SkTable_ColorFilter::flatten(SkWriteBuffer& buffer) const { |
185 | uint8_t storage[5*256]; |
186 | int count = gCountNibBits[fFlags & 0xF]; |
187 | size_t size = SkPackBits::Pack8(fStorage, count * 256, storage, |
188 | sizeof(storage)); |
189 | |
190 | buffer.write32(fFlags); |
191 | buffer.writeByteArray(storage, size); |
192 | } |
193 | |
194 | sk_sp<SkFlattenable> SkTable_ColorFilter::CreateProc(SkReadBuffer& buffer) { |
195 | const int flags = buffer.read32(); |
196 | const size_t count = gCountNibBits[flags & 0xF]; |
197 | SkASSERT(count <= 4); |
198 | |
199 | uint8_t packedStorage[5*256]; |
200 | size_t packedSize = buffer.getArrayCount(); |
201 | if (!buffer.validate(packedSize <= sizeof(packedStorage))) { |
202 | return nullptr; |
203 | } |
204 | if (!buffer.readByteArray(packedStorage, packedSize)) { |
205 | return nullptr; |
206 | } |
207 | |
208 | uint8_t unpackedStorage[4*256]; |
209 | size_t unpackedSize = SkPackBits::Unpack8(packedStorage, packedSize, |
210 | unpackedStorage, sizeof(unpackedStorage)); |
211 | // now check that we got the size we expected |
212 | if (!buffer.validate(unpackedSize == count*256)) { |
213 | return nullptr; |
214 | } |
215 | |
216 | const uint8_t* a = nullptr; |
217 | const uint8_t* r = nullptr; |
218 | const uint8_t* g = nullptr; |
219 | const uint8_t* b = nullptr; |
220 | const uint8_t* ptr = unpackedStorage; |
221 | |
222 | if (flags & kA_Flag) { |
223 | a = ptr; |
224 | ptr += 256; |
225 | } |
226 | if (flags & kR_Flag) { |
227 | r = ptr; |
228 | ptr += 256; |
229 | } |
230 | if (flags & kG_Flag) { |
231 | g = ptr; |
232 | ptr += 256; |
233 | } |
234 | if (flags & kB_Flag) { |
235 | b = ptr; |
236 | ptr += 256; |
237 | } |
238 | return SkTableColorFilter::MakeARGB(a, r, g, b); |
239 | } |
240 | |
241 | void SkTable_ColorFilter::getTableAsBitmap(SkBitmap* table) const { |
242 | if (table) { |
243 | if (nullptr == fBitmap) { |
244 | SkBitmap* bmp = new SkBitmap; |
245 | bmp->allocPixels(SkImageInfo::MakeA8(256, 4)); |
246 | uint8_t* bitmapPixels = bmp->getAddr8(0, 0); |
247 | int offset = 0; |
248 | static const unsigned kFlags[] = { kA_Flag, kR_Flag, kG_Flag, kB_Flag }; |
249 | |
250 | for (int x = 0; x < 4; ++x) { |
251 | if (!(fFlags & kFlags[x])) { |
252 | memcpy(bitmapPixels, gIdentityTable, sizeof(gIdentityTable)); |
253 | } else { |
254 | memcpy(bitmapPixels, fStorage + offset, 256); |
255 | offset += 256; |
256 | } |
257 | bitmapPixels += 256; |
258 | } |
259 | bmp->setImmutable(); |
260 | fBitmap = bmp; |
261 | } |
262 | *table = *fBitmap; |
263 | } |
264 | } |
265 | |
266 | #if SK_SUPPORT_GPU |
267 | |
268 | #include "include/private/GrRecordingContext.h" |
269 | #include "src/gpu/GrColorInfo.h" |
270 | #include "src/gpu/GrFragmentProcessor.h" |
271 | #include "src/gpu/GrRecordingContextPriv.h" |
272 | #include "src/gpu/SkGr.h" |
273 | #include "src/gpu/glsl/GrGLSLFragmentProcessor.h" |
274 | #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h" |
275 | #include "src/gpu/glsl/GrGLSLProgramDataManager.h" |
276 | #include "src/gpu/glsl/GrGLSLUniformHandler.h" |
277 | |
278 | class ColorTableEffect : public GrFragmentProcessor { |
279 | public: |
280 | static std::unique_ptr<GrFragmentProcessor> Make(GrRecordingContext* context, |
281 | const SkBitmap& bitmap); |
282 | |
283 | ~ColorTableEffect() override {} |
284 | |
285 | const char* name() const override { return "ColorTableEffect" ; } |
286 | |
287 | std::unique_ptr<GrFragmentProcessor> clone() const override { |
288 | return std::unique_ptr<GrFragmentProcessor>(new ColorTableEffect(fTextureSampler.view())); |
289 | } |
290 | |
291 | private: |
292 | GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; |
293 | |
294 | void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override; |
295 | |
296 | bool onIsEqual(const GrFragmentProcessor&) const override { return true; } |
297 | |
298 | ColorTableEffect(GrSurfaceProxyView view) |
299 | : INHERITED( |
300 | kColorTableEffect_ClassID, |
301 | kNone_OptimizationFlags) // Not bothering with table-specific optimizations. |
302 | , fTextureSampler(std::move(view)) { |
303 | this->setTextureSamplerCnt(1); |
304 | } |
305 | |
306 | const TextureSampler& onTextureSampler(int) const override { return fTextureSampler; } |
307 | |
308 | GR_DECLARE_FRAGMENT_PROCESSOR_TEST |
309 | |
310 | TextureSampler fTextureSampler; |
311 | |
312 | typedef GrFragmentProcessor INHERITED; |
313 | }; |
314 | |
315 | class GLColorTableEffect : public GrGLSLFragmentProcessor { |
316 | public: |
317 | void emitCode(EmitArgs&) override; |
318 | |
319 | static void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*) {} |
320 | |
321 | private: |
322 | typedef GrGLSLFragmentProcessor INHERITED; |
323 | }; |
324 | |
325 | void GLColorTableEffect::emitCode(EmitArgs& args) { |
326 | static const float kColorScaleFactor = 255.0f / 256.0f; |
327 | static const float kColorOffsetFactor = 1.0f / 512.0f; |
328 | GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; |
329 | if (nullptr == args.fInputColor) { |
330 | // the input color is solid white (all ones). |
331 | static const float kMaxValue = kColorScaleFactor + kColorOffsetFactor; |
332 | fragBuilder->codeAppendf("\t\thalf4 coord = half4(%f, %f, %f, %f);\n" , |
333 | kMaxValue, kMaxValue, kMaxValue, kMaxValue); |
334 | |
335 | } else { |
336 | fragBuilder->codeAppendf("\t\thalf nonZeroAlpha = max(%s.a, .0001);\n" , args.fInputColor); |
337 | fragBuilder->codeAppendf("\t\thalf4 coord = half4(%s.rgb / nonZeroAlpha, nonZeroAlpha);\n" , |
338 | args.fInputColor); |
339 | fragBuilder->codeAppendf("\t\tcoord = coord * %f + half4(%f, %f, %f, %f);\n" , |
340 | kColorScaleFactor, |
341 | kColorOffsetFactor, kColorOffsetFactor, |
342 | kColorOffsetFactor, kColorOffsetFactor); |
343 | } |
344 | |
345 | SkString coord; |
346 | |
347 | fragBuilder->codeAppendf("\t\t%s.a = " , args.fOutputColor); |
348 | coord.printf("half2(coord.a, 0.125)" ); |
349 | fragBuilder->appendTextureLookup(args.fTexSamplers[0], coord.c_str()); |
350 | fragBuilder->codeAppend(".a;\n" ); |
351 | |
352 | fragBuilder->codeAppendf("\t\t%s.r = " , args.fOutputColor); |
353 | coord.printf("half2(coord.r, 0.375)" ); |
354 | fragBuilder->appendTextureLookup(args.fTexSamplers[0], coord.c_str()); |
355 | fragBuilder->codeAppend(".a;\n" ); |
356 | |
357 | fragBuilder->codeAppendf("\t\t%s.g = " , args.fOutputColor); |
358 | coord.printf("half2(coord.g, 0.625)" ); |
359 | fragBuilder->appendTextureLookup(args.fTexSamplers[0], coord.c_str()); |
360 | fragBuilder->codeAppend(".a;\n" ); |
361 | |
362 | fragBuilder->codeAppendf("\t\t%s.b = " , args.fOutputColor); |
363 | coord.printf("half2(coord.b, 0.875)" ); |
364 | fragBuilder->appendTextureLookup(args.fTexSamplers[0], coord.c_str()); |
365 | fragBuilder->codeAppend(".a;\n" ); |
366 | |
367 | fragBuilder->codeAppendf("\t\t%s.rgb *= %s.a;\n" , args.fOutputColor, args.fOutputColor); |
368 | } |
369 | |
370 | /////////////////////////////////////////////////////////////////////////////// |
371 | std::unique_ptr<GrFragmentProcessor> ColorTableEffect::Make(GrRecordingContext* context, |
372 | const SkBitmap& bitmap) { |
373 | SkASSERT(kPremul_SkAlphaType == bitmap.alphaType()); |
374 | SkASSERT(bitmap.isImmutable()); |
375 | |
376 | auto view = GrMakeCachedBitmapProxyView(context, bitmap); |
377 | if (!view) { |
378 | return nullptr; |
379 | } |
380 | |
381 | return std::unique_ptr<GrFragmentProcessor>(new ColorTableEffect(std::move(view))); |
382 | } |
383 | |
384 | void ColorTableEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps, |
385 | GrProcessorKeyBuilder* b) const { |
386 | GLColorTableEffect::GenKey(*this, caps, b); |
387 | } |
388 | |
389 | GrGLSLFragmentProcessor* ColorTableEffect::onCreateGLSLInstance() const { |
390 | return new GLColorTableEffect; |
391 | } |
392 | |
393 | /////////////////////////////////////////////////////////////////////////////// |
394 | |
395 | GR_DEFINE_FRAGMENT_PROCESSOR_TEST(ColorTableEffect); |
396 | |
397 | #if GR_TEST_UTILS |
398 | |
399 | #include "include/gpu/GrContext.h" |
400 | |
401 | std::unique_ptr<GrFragmentProcessor> ColorTableEffect::TestCreate(GrProcessorTestData* d) { |
402 | int flags = 0; |
403 | uint8_t luts[256][4]; |
404 | do { |
405 | for (int i = 0; i < 4; ++i) { |
406 | flags |= d->fRandom->nextBool() ? (1 << i): 0; |
407 | } |
408 | } while (!flags); |
409 | for (int i = 0; i < 4; ++i) { |
410 | if (flags & (1 << i)) { |
411 | for (int j = 0; j < 256; ++j) { |
412 | luts[j][i] = SkToU8(d->fRandom->nextBits(8)); |
413 | } |
414 | } |
415 | } |
416 | auto filter(SkTableColorFilter::MakeARGB( |
417 | (flags & (1 << 0)) ? luts[0] : nullptr, |
418 | (flags & (1 << 1)) ? luts[1] : nullptr, |
419 | (flags & (1 << 2)) ? luts[2] : nullptr, |
420 | (flags & (1 << 3)) ? luts[3] : nullptr |
421 | )); |
422 | sk_sp<SkColorSpace> colorSpace = GrTest::TestColorSpace(d->fRandom); |
423 | auto fp = filter->asFragmentProcessor( |
424 | d->context(), |
425 | GrColorInfo(GrColorType::kRGBA_8888, kUnknown_SkAlphaType, std::move(colorSpace))); |
426 | SkASSERT(fp); |
427 | return fp; |
428 | } |
429 | #endif |
430 | |
431 | std::unique_ptr<GrFragmentProcessor> SkTable_ColorFilter::asFragmentProcessor( |
432 | GrRecordingContext* context, const GrColorInfo&) const { |
433 | SkBitmap bitmap; |
434 | this->getTableAsBitmap(&bitmap); |
435 | |
436 | return ColorTableEffect::Make(context, bitmap); |
437 | } |
438 | |
439 | #endif // SK_SUPPORT_GPU |
440 | |
441 | /////////////////////////////////////////////////////////////////////////////// |
442 | |
443 | sk_sp<SkColorFilter> SkTableColorFilter::Make(const uint8_t table[256]) { |
444 | return sk_make_sp<SkTable_ColorFilter>(table, table, table, table); |
445 | } |
446 | |
447 | sk_sp<SkColorFilter> SkTableColorFilter::MakeARGB(const uint8_t tableA[256], |
448 | const uint8_t tableR[256], |
449 | const uint8_t tableG[256], |
450 | const uint8_t tableB[256]) { |
451 | if (!tableA && !tableR && !tableG && !tableB) { |
452 | return nullptr; |
453 | } |
454 | |
455 | return sk_make_sp<SkTable_ColorFilter>(tableA, tableR, tableG, tableB); |
456 | } |
457 | |
458 | void SkTableColorFilter::RegisterFlattenables() { SK_REGISTER_FLATTENABLE(SkTable_ColorFilter); } |
459 | |