2 * Copyright 2015 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "SkTableColorFilter.h"
11 #include "SkColorPriv.h"
12 #include "SkReadBuffer.h"
14 #include "SkUnPreMultiply.h"
15 #include "SkWriteBuffer.h"
17 class SkTable_ColorFilter : public SkColorFilter {
19 SkTable_ColorFilter(const uint8_t tableA[], const uint8_t tableR[],
20 const uint8_t tableG[], const uint8_t tableB[]) {
24 uint8_t* dst = fStorage;
26 memcpy(dst, tableA, 256);
31 memcpy(dst, tableR, 256);
36 memcpy(dst, tableG, 256);
41 memcpy(dst, tableB, 256);
46 virtual ~SkTable_ColorFilter() { delete fBitmap; }
48 bool asComponentTable(SkBitmap* table) const override;
49 sk_sp<SkColorFilter> makeComposed(sk_sp<SkColorFilter> inner) const override;
52 sk_sp<GrFragmentProcessor> asFragmentProcessor(GrContext*, SkColorSpace*) const override;
55 void filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const override;
57 SK_TO_STRING_OVERRIDE()
59 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkTable_ColorFilter)
69 void flatten(SkWriteBuffer&) const override;
72 mutable const SkBitmap* fBitmap; // lazily allocated
74 uint8_t fStorage[256 * 4];
77 friend class SkTableColorFilter;
79 typedef SkColorFilter INHERITED;
82 static const uint8_t gIdentityTable[] = {
83 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
84 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
85 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
86 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
87 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
88 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
89 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
90 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
91 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
92 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
93 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
94 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
95 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
96 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
97 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
98 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
99 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
100 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
101 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
102 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
103 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
104 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
105 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7,
106 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
107 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
108 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
109 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7,
110 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
111 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
112 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
113 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
114 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
117 void SkTable_ColorFilter::filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const {
118 const uint8_t* table = fStorage;
119 const uint8_t* tableA = gIdentityTable;
120 const uint8_t* tableR = gIdentityTable;
121 const uint8_t* tableG = gIdentityTable;
122 const uint8_t* tableB = gIdentityTable;
123 if (fFlags & kA_Flag) {
124 tableA = table; table += 256;
126 if (fFlags & kR_Flag) {
127 tableR = table; table += 256;
129 if (fFlags & kG_Flag) {
130 tableG = table; table += 256;
132 if (fFlags & kB_Flag) {
136 const SkUnPreMultiply::Scale* scaleTable = SkUnPreMultiply::GetScaleTable();
137 for (int i = 0; i < count; ++i) {
138 SkPMColor c = src[i];
143 a = SkGetPackedA32(c);
144 r = SkGetPackedR32(c);
145 g = SkGetPackedG32(c);
146 b = SkGetPackedB32(c);
149 SkUnPreMultiply::Scale scale = scaleTable[a];
150 r = SkUnPreMultiply::ApplyScale(scale, r);
151 g = SkUnPreMultiply::ApplyScale(scale, g);
152 b = SkUnPreMultiply::ApplyScale(scale, b);
155 dst[i] = SkPremultiplyARGBInline(tableA[a], tableR[r],
156 tableG[g], tableB[b]);
160 #ifndef SK_IGNORE_TO_STRING
161 void SkTable_ColorFilter::toString(SkString* str) const {
162 const uint8_t* table = fStorage;
163 const uint8_t* tableA = gIdentityTable;
164 const uint8_t* tableR = gIdentityTable;
165 const uint8_t* tableG = gIdentityTable;
166 const uint8_t* tableB = gIdentityTable;
167 if (fFlags & kA_Flag) {
168 tableA = table; table += 256;
170 if (fFlags & kR_Flag) {
171 tableR = table; table += 256;
173 if (fFlags & kG_Flag) {
174 tableG = table; table += 256;
176 if (fFlags & kB_Flag) {
180 str->append("SkTable_ColorFilter (");
182 for (int i = 0; i < 256; ++i) {
183 str->appendf("%d: %d,%d,%d,%d\n",
184 i, tableR[i], tableG[i], tableB[i], tableA[i]);
191 static const uint8_t gCountNibBits[] = {
198 #include "SkPackBits.h"
200 void SkTable_ColorFilter::flatten(SkWriteBuffer& buffer) const {
201 uint8_t storage[5*256];
202 int count = gCountNibBits[fFlags & 0xF];
203 size_t size = SkPackBits::Pack8(fStorage, count * 256, storage,
206 buffer.write32(fFlags);
207 buffer.writeByteArray(storage, size);
210 sk_sp<SkFlattenable> SkTable_ColorFilter::CreateProc(SkReadBuffer& buffer) {
211 const int flags = buffer.read32();
212 const size_t count = gCountNibBits[flags & 0xF];
213 SkASSERT(count <= 4);
215 uint8_t packedStorage[5*256];
216 size_t packedSize = buffer.getArrayCount();
217 if (!buffer.validate(packedSize <= sizeof(packedStorage))) {
220 if (!buffer.readByteArray(packedStorage, packedSize)) {
224 uint8_t unpackedStorage[4*256];
225 size_t unpackedSize = SkPackBits::Unpack8(packedStorage, packedSize,
226 unpackedStorage, sizeof(unpackedStorage));
227 // now check that we got the size we expected
228 if (!buffer.validate(unpackedSize == count*256)) {
232 const uint8_t* a = nullptr;
233 const uint8_t* r = nullptr;
234 const uint8_t* g = nullptr;
235 const uint8_t* b = nullptr;
236 const uint8_t* ptr = unpackedStorage;
238 if (flags & kA_Flag) {
242 if (flags & kR_Flag) {
246 if (flags & kG_Flag) {
250 if (flags & kB_Flag) {
254 return SkTableColorFilter::MakeARGB(a, r, g, b);
257 bool SkTable_ColorFilter::asComponentTable(SkBitmap* table) const {
259 if (nullptr == fBitmap) {
260 SkBitmap* bmp = new SkBitmap;
261 bmp->allocPixels(SkImageInfo::MakeA8(256, 4));
262 uint8_t* bitmapPixels = bmp->getAddr8(0, 0);
264 static const unsigned kFlags[] = { kA_Flag, kR_Flag, kG_Flag, kB_Flag };
266 for (int x = 0; x < 4; ++x) {
267 if (!(fFlags & kFlags[x])) {
268 memcpy(bitmapPixels, gIdentityTable, sizeof(gIdentityTable));
270 memcpy(bitmapPixels, fStorage + offset, 256);
282 // Combines the two lookup tables so that making a lookup using res[] has
283 // the same effect as making a lookup through inner[] then outer[].
284 static void combine_tables(uint8_t res[256], const uint8_t outer[256], const uint8_t inner[256]) {
285 for (int i = 0; i < 256; i++) {
286 res[i] = outer[inner[i]];
290 sk_sp<SkColorFilter> SkTable_ColorFilter::makeComposed(sk_sp<SkColorFilter> innerFilter) const {
292 if (!innerFilter->asComponentTable(&innerBM)) {
296 innerBM.lockPixels();
297 if (nullptr == innerBM.getPixels()) {
301 const uint8_t* table = fStorage;
302 const uint8_t* tableA = gIdentityTable;
303 const uint8_t* tableR = gIdentityTable;
304 const uint8_t* tableG = gIdentityTable;
305 const uint8_t* tableB = gIdentityTable;
306 if (fFlags & kA_Flag) {
307 tableA = table; table += 256;
309 if (fFlags & kR_Flag) {
310 tableR = table; table += 256;
312 if (fFlags & kG_Flag) {
313 tableG = table; table += 256;
315 if (fFlags & kB_Flag) {
319 uint8_t concatA[256];
320 uint8_t concatR[256];
321 uint8_t concatG[256];
322 uint8_t concatB[256];
324 combine_tables(concatA, tableA, innerBM.getAddr8(0, 0));
325 combine_tables(concatR, tableR, innerBM.getAddr8(0, 1));
326 combine_tables(concatG, tableG, innerBM.getAddr8(0, 2));
327 combine_tables(concatB, tableB, innerBM.getAddr8(0, 3));
329 return SkTableColorFilter::MakeARGB(concatA, concatR, concatG, concatB);
334 #include "GrContext.h"
335 #include "GrFragmentProcessor.h"
336 #include "GrInvariantOutput.h"
337 #include "GrTextureStripAtlas.h"
339 #include "glsl/GrGLSLFragmentProcessor.h"
340 #include "glsl/GrGLSLFragmentShaderBuilder.h"
341 #include "glsl/GrGLSLProgramDataManager.h"
342 #include "glsl/GrGLSLUniformHandler.h"
344 class ColorTableEffect : public GrFragmentProcessor {
346 static sk_sp<GrFragmentProcessor> Make(GrContext* context, SkBitmap bitmap, unsigned flags);
348 virtual ~ColorTableEffect();
350 const char* name() const override { return "ColorTable"; }
352 const GrTextureStripAtlas* atlas() const { return fAtlas; }
353 int atlasRow() const { return fRow; }
356 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
358 void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
360 bool onIsEqual(const GrFragmentProcessor&) const override;
362 void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
364 ColorTableEffect(GrTexture* texture, GrTextureStripAtlas* atlas, int row, unsigned flags);
366 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
368 TextureSampler fTextureSampler;
370 // currently not used in shader code, just to assist onComputeInvariantOutput().
373 GrTextureStripAtlas* fAtlas;
376 typedef GrFragmentProcessor INHERITED;
379 class GLColorTableEffect : public GrGLSLFragmentProcessor {
381 void emitCode(EmitArgs&) override;
383 static void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*) {}
386 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
389 UniformHandle fRGBAYValuesUni;
390 typedef GrGLSLFragmentProcessor INHERITED;
393 void GLColorTableEffect::onSetData(const GrGLSLProgramDataManager& pdm, const GrProcessor& proc) {
394 // The textures are organized in a strip where the rows are ordered a, r, g, b.
395 float rgbaYValues[4];
396 const ColorTableEffect& cte = proc.cast<ColorTableEffect>();
398 SkScalar yDelta = cte.atlas()->getNormalizedTexelHeight();
399 rgbaYValues[3] = cte.atlas()->getYOffset(cte.atlasRow()) + SK_ScalarHalf * yDelta;
400 rgbaYValues[0] = rgbaYValues[3] + yDelta;
401 rgbaYValues[1] = rgbaYValues[0] + yDelta;
402 rgbaYValues[2] = rgbaYValues[1] + yDelta;
404 rgbaYValues[3] = 0.125;
405 rgbaYValues[0] = 0.375;
406 rgbaYValues[1] = 0.625;
407 rgbaYValues[2] = 0.875;
409 pdm.set4fv(fRGBAYValuesUni, 1, rgbaYValues);
412 void GLColorTableEffect::emitCode(EmitArgs& args) {
413 const char* yoffsets;
414 fRGBAYValuesUni = args.fUniformHandler->addUniform(kFragment_GrShaderFlag,
415 kVec4f_GrSLType, kDefault_GrSLPrecision,
416 "yoffsets", &yoffsets);
417 static const float kColorScaleFactor = 255.0f / 256.0f;
418 static const float kColorOffsetFactor = 1.0f / 512.0f;
419 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
420 if (nullptr == args.fInputColor) {
421 // the input color is solid white (all ones).
422 static const float kMaxValue = kColorScaleFactor + kColorOffsetFactor;
423 fragBuilder->codeAppendf("\t\tvec4 coord = vec4(%f, %f, %f, %f);\n",
424 kMaxValue, kMaxValue, kMaxValue, kMaxValue);
427 fragBuilder->codeAppendf("\t\tfloat nonZeroAlpha = max(%s.a, .0001);\n", args.fInputColor);
428 fragBuilder->codeAppendf("\t\tvec4 coord = vec4(%s.rgb / nonZeroAlpha, nonZeroAlpha);\n",
430 fragBuilder->codeAppendf("\t\tcoord = coord * %f + vec4(%f, %f, %f, %f);\n",
432 kColorOffsetFactor, kColorOffsetFactor,
433 kColorOffsetFactor, kColorOffsetFactor);
438 fragBuilder->codeAppendf("\t\t%s.a = ", args.fOutputColor);
439 coord.printf("vec2(coord.a, %s.a)", yoffsets);
440 fragBuilder->appendTextureLookup(args.fTexSamplers[0], coord.c_str());
441 fragBuilder->codeAppend(".a;\n");
443 fragBuilder->codeAppendf("\t\t%s.r = ", args.fOutputColor);
444 coord.printf("vec2(coord.r, %s.r)", yoffsets);
445 fragBuilder->appendTextureLookup(args.fTexSamplers[0], coord.c_str());
446 fragBuilder->codeAppend(".a;\n");
448 fragBuilder->codeAppendf("\t\t%s.g = ", args.fOutputColor);
449 coord.printf("vec2(coord.g, %s.g)", yoffsets);
450 fragBuilder->appendTextureLookup(args.fTexSamplers[0], coord.c_str());
451 fragBuilder->codeAppend(".a;\n");
453 fragBuilder->codeAppendf("\t\t%s.b = ", args.fOutputColor);
454 coord.printf("vec2(coord.b, %s.b)", yoffsets);
455 fragBuilder->appendTextureLookup(args.fTexSamplers[0], coord.c_str());
456 fragBuilder->codeAppend(".a;\n");
458 fragBuilder->codeAppendf("\t\t%s.rgb *= %s.a;\n", args.fOutputColor, args.fOutputColor);
461 ///////////////////////////////////////////////////////////////////////////////
462 sk_sp<GrFragmentProcessor> ColorTableEffect::Make(GrContext* context, SkBitmap bitmap,
465 GrTextureStripAtlas::Desc desc;
466 desc.fWidth = bitmap.width();
468 desc.fRowHeight = bitmap.height();
469 desc.fContext = context;
470 desc.fConfig = SkImageInfo2GrPixelConfig(bitmap.info(), *context->caps());
471 GrTextureStripAtlas* atlas = GrTextureStripAtlas::GetAtlas(desc);
472 int row = atlas->lockRow(bitmap);
473 sk_sp<GrTexture> texture;
477 GrRefCachedBitmapTexture(context, bitmap, GrSamplerParams::ClampNoFilter()));
479 texture.reset(SkRef(atlas->getTexture()));
482 return sk_sp<GrFragmentProcessor>(new ColorTableEffect(texture.get(), atlas, row, flags));
485 ColorTableEffect::ColorTableEffect(GrTexture* texture, GrTextureStripAtlas* atlas, int row,
487 : fTextureSampler(texture)
491 this->initClassID<ColorTableEffect>();
492 this->addTextureSampler(&fTextureSampler);
495 ColorTableEffect::~ColorTableEffect() {
497 fAtlas->unlockRow(fRow);
501 void ColorTableEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
502 GrProcessorKeyBuilder* b) const {
503 GLColorTableEffect::GenKey(*this, caps, b);
506 GrGLSLFragmentProcessor* ColorTableEffect::onCreateGLSLInstance() const {
507 return new GLColorTableEffect;
510 bool ColorTableEffect::onIsEqual(const GrFragmentProcessor& other) const {
511 // For non-atlased instances, the texture (compared by base class) is sufficient to
512 // differentiate different tables. For atlased instances we ensure they are using the
514 const ColorTableEffect& that = other.cast<ColorTableEffect>();
515 SkASSERT(SkToBool(fAtlas) == SkToBool(that.fAtlas));
516 // Ok to always do this comparison since both would be -1 if non-atlased.
517 return fRow == that.fRow;
520 void ColorTableEffect::onComputeInvariantOutput(GrInvariantOutput* inout) const {
521 // If we kept the table in the effect then we could actually run known inputs through the
523 GrColorComponentFlags invalidateFlags = kNone_GrColorComponentFlags;
524 if (fFlags & SkTable_ColorFilter::kR_Flag) {
525 invalidateFlags |= kR_GrColorComponentFlag;
527 if (fFlags & SkTable_ColorFilter::kG_Flag) {
528 invalidateFlags |= kG_GrColorComponentFlag;
530 if (fFlags & SkTable_ColorFilter::kB_Flag) {
531 invalidateFlags |= kB_GrColorComponentFlag;
533 if (fFlags & SkTable_ColorFilter::kA_Flag) {
534 invalidateFlags |= kA_GrColorComponentFlag;
536 inout->invalidateComponents(invalidateFlags, GrInvariantOutput::kWill_ReadInput);
539 ///////////////////////////////////////////////////////////////////////////////
541 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(ColorTableEffect);
543 sk_sp<GrFragmentProcessor> ColorTableEffect::TestCreate(GrProcessorTestData* d) {
545 uint8_t luts[256][4];
547 for (int i = 0; i < 4; ++i) {
548 flags |= d->fRandom->nextBool() ? (1 << i): 0;
551 for (int i = 0; i < 4; ++i) {
552 if (flags & (1 << i)) {
553 for (int j = 0; j < 256; ++j) {
554 luts[j][i] = SkToU8(d->fRandom->nextBits(8));
558 auto filter(SkTableColorFilter::MakeARGB(
559 (flags & (1 << 0)) ? luts[0] : nullptr,
560 (flags & (1 << 1)) ? luts[1] : nullptr,
561 (flags & (1 << 2)) ? luts[2] : nullptr,
562 (flags & (1 << 3)) ? luts[3] : nullptr
564 sk_sp<SkColorSpace> colorSpace = GrTest::TestColorSpace(d->fRandom);
565 sk_sp<GrFragmentProcessor> fp = filter->asFragmentProcessor(d->fContext, colorSpace.get());
570 sk_sp<GrFragmentProcessor> SkTable_ColorFilter::asFragmentProcessor(GrContext* context,
571 SkColorSpace*) const {
573 this->asComponentTable(&bitmap);
575 return ColorTableEffect::Make(context, bitmap, fFlags);
578 #endif // SK_SUPPORT_GPU
580 ///////////////////////////////////////////////////////////////////////////////
582 #ifdef SK_CPU_BENDIAN
584 #define SK_A32_INDEX (3 - (SK_A32_SHIFT >> 3))
585 #define SK_R32_INDEX (3 - (SK_R32_SHIFT >> 3))
586 #define SK_G32_INDEX (3 - (SK_G32_SHIFT >> 3))
587 #define SK_B32_INDEX (3 - (SK_B32_SHIFT >> 3))
590 ///////////////////////////////////////////////////////////////////////////////
592 sk_sp<SkColorFilter> SkTableColorFilter::Make(const uint8_t table[256]) {
593 return sk_make_sp<SkTable_ColorFilter>(table, table, table, table);
596 sk_sp<SkColorFilter> SkTableColorFilter::MakeARGB(const uint8_t tableA[256],
597 const uint8_t tableR[256],
598 const uint8_t tableG[256],
599 const uint8_t tableB[256]) {
600 return sk_make_sp<SkTable_ColorFilter>(tableA, tableR, tableG, tableB);
603 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkTableColorFilter)
604 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkTable_ColorFilter)
605 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END