SkTableColorFilter.cpp revision cdee009886babe6df7743a9b5b3e2cc0a5f21adf
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 "SkTableColorFilter.h"
9
10#include "SkBitmap.h"
11#include "SkColorPriv.h"
12#include "SkReadBuffer.h"
13#include "SkString.h"
14#include "SkUnPreMultiply.h"
15#include "SkWriteBuffer.h"
16
17class SkTable_ColorFilter : public SkColorFilter {
18public:
19    SkTable_ColorFilter(const uint8_t tableA[], const uint8_t tableR[],
20                        const uint8_t tableG[], const uint8_t tableB[]) {
21        fBitmap = nullptr;
22        fFlags = 0;
23
24        uint8_t* dst = fStorage;
25        if (tableA) {
26            memcpy(dst, tableA, 256);
27            dst += 256;
28            fFlags |= kA_Flag;
29        }
30        if (tableR) {
31            memcpy(dst, tableR, 256);
32            dst += 256;
33            fFlags |= kR_Flag;
34        }
35        if (tableG) {
36            memcpy(dst, tableG, 256);
37            dst += 256;
38            fFlags |= kG_Flag;
39        }
40        if (tableB) {
41            memcpy(dst, tableB, 256);
42            fFlags |= kB_Flag;
43        }
44    }
45
46    virtual ~SkTable_ColorFilter() { delete fBitmap; }
47
48    bool asComponentTable(SkBitmap* table) const override;
49    SkColorFilter* newComposed(const SkColorFilter* inner) const override;
50
51#if SK_SUPPORT_GPU
52    const GrFragmentProcessor* asFragmentProcessor(GrContext*) const override;
53#endif
54
55    void filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const override;
56
57    SK_TO_STRING_OVERRIDE()
58
59    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkTable_ColorFilter)
60
61    enum {
62        kA_Flag = 1 << 0,
63        kR_Flag = 1 << 1,
64        kG_Flag = 1 << 2,
65        kB_Flag = 1 << 3,
66    };
67
68protected:
69    void flatten(SkWriteBuffer&) const override;
70
71private:
72    mutable const SkBitmap* fBitmap; // lazily allocated
73
74    uint8_t fStorage[256 * 4];
75    unsigned fFlags;
76
77    friend class SkTableColorFilter;
78
79    typedef SkColorFilter INHERITED;
80};
81
82static 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
115};
116
117void 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;
125    }
126    if (fFlags & kR_Flag) {
127        tableR = table; table += 256;
128    }
129    if (fFlags & kG_Flag) {
130        tableG = table; table += 256;
131    }
132    if (fFlags & kB_Flag) {
133        tableB = table;
134    }
135
136    const SkUnPreMultiply::Scale* scaleTable = SkUnPreMultiply::GetScaleTable();
137    for (int i = 0; i < count; ++i) {
138        SkPMColor c = src[i];
139        unsigned a, r, g, b;
140        if (0 == c) {
141            a = r = g = b = 0;
142        } else {
143            a = SkGetPackedA32(c);
144            r = SkGetPackedR32(c);
145            g = SkGetPackedG32(c);
146            b = SkGetPackedB32(c);
147
148            if (a < 255) {
149                SkUnPreMultiply::Scale scale = scaleTable[a];
150                r = SkUnPreMultiply::ApplyScale(scale, r);
151                g = SkUnPreMultiply::ApplyScale(scale, g);
152                b = SkUnPreMultiply::ApplyScale(scale, b);
153            }
154        }
155        dst[i] = SkPremultiplyARGBInline(tableA[a], tableR[r],
156                                         tableG[g], tableB[b]);
157    }
158}
159
160#ifndef SK_IGNORE_TO_STRING
161void 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;
169    }
170    if (fFlags & kR_Flag) {
171        tableR = table; table += 256;
172    }
173    if (fFlags & kG_Flag) {
174        tableG = table; table += 256;
175    }
176    if (fFlags & kB_Flag) {
177        tableB = table;
178    }
179
180    str->append("SkTable_ColorFilter (");
181
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]);
185    }
186
187    str->append(")");
188}
189#endif
190
191static const uint8_t gCountNibBits[] = {
192    0, 1, 1, 2,
193    1, 2, 2, 3,
194    1, 2, 2, 3,
195    2, 3, 3, 4
196};
197
198#include "SkPackBits.h"
199
200void 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,
204                                    sizeof(storage));
205
206    buffer.write32(fFlags);
207    buffer.writeByteArray(storage, size);
208}
209
210SkFlattenable* SkTable_ColorFilter::CreateProc(SkReadBuffer& buffer) {
211    const int flags = buffer.read32();
212    const size_t count = gCountNibBits[flags & 0xF];
213    SkASSERT(count <= 4);
214
215    uint8_t packedStorage[5*256];
216    size_t packedSize = buffer.getArrayCount();
217    if (!buffer.validate(packedSize <= sizeof(packedStorage))) {
218        return nullptr;
219    }
220    if (!buffer.readByteArray(packedStorage, packedSize)) {
221        return nullptr;
222    }
223
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)) {
229        return nullptr;
230    }
231
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;
237
238    if (flags & kA_Flag) {
239        a = ptr;
240        ptr += 256;
241    }
242    if (flags & kR_Flag) {
243        r = ptr;
244        ptr += 256;
245    }
246    if (flags & kG_Flag) {
247        g = ptr;
248        ptr += 256;
249    }
250    if (flags & kB_Flag) {
251        b = ptr;
252        ptr += 256;
253    }
254    return SkTableColorFilter::CreateARGB(a, r, g, b);
255}
256
257bool SkTable_ColorFilter::asComponentTable(SkBitmap* table) const {
258    if (table) {
259        if (nullptr == fBitmap) {
260            SkBitmap* bmp = new SkBitmap;
261            bmp->allocPixels(SkImageInfo::MakeA8(256, 4));
262            uint8_t* bitmapPixels = bmp->getAddr8(0, 0);
263            int offset = 0;
264            static const unsigned kFlags[] = { kA_Flag, kR_Flag, kG_Flag, kB_Flag };
265
266            for (int x = 0; x < 4; ++x) {
267                if (!(fFlags & kFlags[x])) {
268                    memcpy(bitmapPixels, gIdentityTable, sizeof(gIdentityTable));
269                } else {
270                    memcpy(bitmapPixels, fStorage + offset, 256);
271                    offset += 256;
272                }
273                bitmapPixels += 256;
274            }
275            fBitmap = bmp;
276        }
277        *table = *fBitmap;
278    }
279    return true;
280}
281
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[].
284static 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]];
287    }
288}
289
290SkColorFilter* SkTable_ColorFilter::newComposed(const SkColorFilter* innerFilter) const {
291    SkBitmap innerBM;
292    if (!innerFilter->asComponentTable(&innerBM)) {
293        return nullptr;
294    }
295
296    innerBM.lockPixels();
297    if (nullptr == innerBM.getPixels()) {
298        return nullptr;
299    }
300
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;
308    }
309    if (fFlags & kR_Flag) {
310        tableR = table; table += 256;
311    }
312    if (fFlags & kG_Flag) {
313        tableG = table; table += 256;
314    }
315    if (fFlags & kB_Flag) {
316        tableB = table;
317    }
318
319    uint8_t concatA[256];
320    uint8_t concatR[256];
321    uint8_t concatG[256];
322    uint8_t concatB[256];
323
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));
328
329    return SkTableColorFilter::CreateARGB(concatA, concatR, concatG, concatB);
330}
331
332#if SK_SUPPORT_GPU
333
334#include "GrFragmentProcessor.h"
335#include "GrInvariantOutput.h"
336#include "SkGr.h"
337#include "effects/GrTextureStripAtlas.h"
338#include "glsl/GrGLSLFragmentProcessor.h"
339#include "glsl/GrGLSLFragmentShaderBuilder.h"
340#include "glsl/GrGLSLProgramDataManager.h"
341#include "glsl/GrGLSLUniformHandler.h"
342
343class ColorTableEffect : public GrFragmentProcessor {
344public:
345    static const GrFragmentProcessor* Create(GrContext* context, SkBitmap bitmap, unsigned flags);
346
347    virtual ~ColorTableEffect();
348
349    const char* name() const override { return "ColorTable"; }
350
351    const GrTextureStripAtlas* atlas() const { return fAtlas; }
352    int atlasRow() const { return fRow; }
353
354private:
355    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
356
357    void onGetGLSLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
358
359    bool onIsEqual(const GrFragmentProcessor&) const override;
360
361    void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
362
363    ColorTableEffect(GrTexture* texture, GrTextureStripAtlas* atlas, int row, unsigned flags);
364
365    GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
366
367    GrTextureAccess         fTextureAccess;
368
369    // currently not used in shader code, just to assist onComputeInvariantOutput().
370    unsigned                fFlags;
371
372    GrTextureStripAtlas*    fAtlas;
373    int                     fRow;
374
375    typedef GrFragmentProcessor INHERITED;
376};
377
378class GLColorTableEffect : public GrGLSLFragmentProcessor {
379public:
380    GLColorTableEffect(const GrProcessor&);
381
382    virtual void emitCode(EmitArgs&) override;
383
384    static void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder* b) {}
385
386protected:
387    void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
388
389private:
390    UniformHandle fRGBAYValuesUni;
391    typedef GrGLSLFragmentProcessor INHERITED;
392};
393
394GLColorTableEffect::GLColorTableEffect(const GrProcessor&) {
395}
396
397void GLColorTableEffect::onSetData(const GrGLSLProgramDataManager& pdm, const GrProcessor& proc) {
398    // The textures are organized in a strip where the rows are ordered a, r, g, b.
399    float rgbaYValues[4];
400    const ColorTableEffect& cte = proc.cast<ColorTableEffect>();
401    if (cte.atlas()) {
402        SkScalar yDelta = cte.atlas()->getNormalizedTexelHeight();
403        rgbaYValues[3] = cte.atlas()->getYOffset(cte.atlasRow()) + SK_ScalarHalf * yDelta;
404        rgbaYValues[0] = rgbaYValues[3] + yDelta;
405        rgbaYValues[1] = rgbaYValues[0] + yDelta;
406        rgbaYValues[2] = rgbaYValues[1] + yDelta;
407    } else {
408        rgbaYValues[3] = 0.125;
409        rgbaYValues[0] = 0.375;
410        rgbaYValues[1] = 0.625;
411        rgbaYValues[2] = 0.875;
412    }
413    pdm.set4fv(fRGBAYValuesUni, 1, rgbaYValues);
414}
415
416void GLColorTableEffect::emitCode(EmitArgs& args) {
417    const char* yoffsets;
418    fRGBAYValuesUni = args.fUniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility,
419                                                       kVec4f_GrSLType, kDefault_GrSLPrecision,
420                                                       "yoffsets", &yoffsets);
421    static const float kColorScaleFactor = 255.0f / 256.0f;
422    static const float kColorOffsetFactor = 1.0f / 512.0f;
423    GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
424    if (nullptr == args.fInputColor) {
425        // the input color is solid white (all ones).
426        static const float kMaxValue = kColorScaleFactor + kColorOffsetFactor;
427        fragBuilder->codeAppendf("\t\tvec4 coord = vec4(%f, %f, %f, %f);\n",
428                                 kMaxValue, kMaxValue, kMaxValue, kMaxValue);
429
430    } else {
431        fragBuilder->codeAppendf("\t\tfloat nonZeroAlpha = max(%s.a, .0001);\n", args.fInputColor);
432        fragBuilder->codeAppendf("\t\tvec4 coord = vec4(%s.rgb / nonZeroAlpha, nonZeroAlpha);\n",
433                                 args.fInputColor);
434        fragBuilder->codeAppendf("\t\tcoord = coord * %f + vec4(%f, %f, %f, %f);\n",
435                                 kColorScaleFactor,
436                                 kColorOffsetFactor, kColorOffsetFactor,
437                                 kColorOffsetFactor, kColorOffsetFactor);
438    }
439
440    SkString coord;
441
442    fragBuilder->codeAppendf("\t\t%s.a = ", args.fOutputColor);
443    coord.printf("vec2(coord.a, %s.a)", yoffsets);
444    fragBuilder->appendTextureLookup(args.fSamplers[0], coord.c_str());
445    fragBuilder->codeAppend(".a;\n");
446
447    fragBuilder->codeAppendf("\t\t%s.r = ", args.fOutputColor);
448    coord.printf("vec2(coord.r, %s.r)", yoffsets);
449    fragBuilder->appendTextureLookup(args.fSamplers[0], coord.c_str());
450    fragBuilder->codeAppend(".a;\n");
451
452    fragBuilder->codeAppendf("\t\t%s.g = ", args.fOutputColor);
453    coord.printf("vec2(coord.g, %s.g)", yoffsets);
454    fragBuilder->appendTextureLookup(args.fSamplers[0], coord.c_str());
455    fragBuilder->codeAppend(".a;\n");
456
457    fragBuilder->codeAppendf("\t\t%s.b = ", args.fOutputColor);
458    coord.printf("vec2(coord.b, %s.b)", yoffsets);
459    fragBuilder->appendTextureLookup(args.fSamplers[0], coord.c_str());
460    fragBuilder->codeAppend(".a;\n");
461
462    fragBuilder->codeAppendf("\t\t%s.rgb *= %s.a;\n", args.fOutputColor, args.fOutputColor);
463}
464
465///////////////////////////////////////////////////////////////////////////////
466const GrFragmentProcessor* ColorTableEffect::Create(GrContext* context, SkBitmap bitmap,
467                                                    unsigned flags) {
468
469    GrTextureStripAtlas::Desc desc;
470    desc.fWidth  = bitmap.width();
471    desc.fHeight = 128;
472    desc.fRowHeight = bitmap.height();
473    desc.fContext = context;
474    desc.fConfig = SkImageInfo2GrPixelConfig(bitmap.info());
475    GrTextureStripAtlas* atlas = GrTextureStripAtlas::GetAtlas(desc);
476    int row = atlas->lockRow(bitmap);
477    SkAutoTUnref<GrTexture> texture;
478    if (-1 == row) {
479        atlas = nullptr;
480        texture.reset(GrRefCachedBitmapTexture(context, bitmap, GrTextureParams::ClampNoFilter()));
481    } else {
482        texture.reset(SkRef(atlas->getTexture()));
483    }
484
485    return new ColorTableEffect(texture, atlas, row, flags);
486}
487
488ColorTableEffect::ColorTableEffect(GrTexture* texture, GrTextureStripAtlas* atlas, int row,
489                                   unsigned flags)
490    : fTextureAccess(texture)
491    , fFlags(flags)
492    , fAtlas(atlas)
493    , fRow(row) {
494    this->initClassID<ColorTableEffect>();
495    this->addTextureAccess(&fTextureAccess);
496}
497
498ColorTableEffect::~ColorTableEffect() {
499    if (fAtlas) {
500        fAtlas->unlockRow(fRow);
501    }
502}
503
504void ColorTableEffect::onGetGLSLProcessorKey(const GrGLSLCaps& caps,
505                                             GrProcessorKeyBuilder* b) const {
506    GLColorTableEffect::GenKey(*this, caps, b);
507}
508
509GrGLSLFragmentProcessor* ColorTableEffect::onCreateGLSLInstance() const {
510    return new GLColorTableEffect(*this);
511}
512
513bool ColorTableEffect::onIsEqual(const GrFragmentProcessor& other) const {
514    // For non-atlased instances, the texture (compared by base class) is sufficient to
515    // differentiate different tables. For atlased instances we ensure they are using the
516    // same row.
517    const ColorTableEffect& that = other.cast<ColorTableEffect>();
518    SkASSERT(SkToBool(fAtlas) == SkToBool(that.fAtlas));
519    // Ok to always do this comparison since both would be -1 if non-atlased.
520    return fRow == that.fRow;
521}
522
523void ColorTableEffect::onComputeInvariantOutput(GrInvariantOutput* inout) const {
524    // If we kept the table in the effect then we could actually run known inputs through the
525    // table.
526    GrColorComponentFlags invalidateFlags = kNone_GrColorComponentFlags;
527    if (fFlags & SkTable_ColorFilter::kR_Flag) {
528        invalidateFlags |= kR_GrColorComponentFlag;
529    }
530    if (fFlags & SkTable_ColorFilter::kG_Flag) {
531        invalidateFlags |= kG_GrColorComponentFlag;
532    }
533    if (fFlags & SkTable_ColorFilter::kB_Flag) {
534        invalidateFlags |= kB_GrColorComponentFlag;
535    }
536    if (fFlags & SkTable_ColorFilter::kA_Flag) {
537        invalidateFlags |= kA_GrColorComponentFlag;
538    }
539    inout->invalidateComponents(invalidateFlags, GrInvariantOutput::kWill_ReadInput);
540}
541
542///////////////////////////////////////////////////////////////////////////////
543
544GR_DEFINE_FRAGMENT_PROCESSOR_TEST(ColorTableEffect);
545
546const GrFragmentProcessor* ColorTableEffect::TestCreate(GrProcessorTestData* d) {
547    int flags = 0;
548    uint8_t luts[256][4];
549    do {
550        for (int i = 0; i < 4; ++i) {
551            flags |= d->fRandom->nextBool() ? (1  << i): 0;
552        }
553    } while (!flags);
554    for (int i = 0; i < 4; ++i) {
555        if (flags & (1 << i)) {
556            for (int j = 0; j < 256; ++j) {
557                luts[j][i] = SkToU8(d->fRandom->nextBits(8));
558            }
559        }
560    }
561    SkAutoTUnref<SkColorFilter> filter(SkTableColorFilter::CreateARGB(
562        (flags & (1 << 0)) ? luts[0] : nullptr,
563        (flags & (1 << 1)) ? luts[1] : nullptr,
564        (flags & (1 << 2)) ? luts[2] : nullptr,
565        (flags & (1 << 3)) ? luts[3] : nullptr
566    ));
567
568    const GrFragmentProcessor* fp = filter->asFragmentProcessor(d->fContext);
569    SkASSERT(fp);
570    return fp;
571}
572
573const GrFragmentProcessor* SkTable_ColorFilter::asFragmentProcessor(GrContext* context) const {
574    SkBitmap bitmap;
575    this->asComponentTable(&bitmap);
576
577    return ColorTableEffect::Create(context, bitmap, fFlags);
578}
579
580#endif // SK_SUPPORT_GPU
581
582///////////////////////////////////////////////////////////////////////////////
583
584#ifdef SK_CPU_BENDIAN
585#else
586    #define SK_A32_INDEX    (3 - (SK_A32_SHIFT >> 3))
587    #define SK_R32_INDEX    (3 - (SK_R32_SHIFT >> 3))
588    #define SK_G32_INDEX    (3 - (SK_G32_SHIFT >> 3))
589    #define SK_B32_INDEX    (3 - (SK_B32_SHIFT >> 3))
590#endif
591
592///////////////////////////////////////////////////////////////////////////////
593
594SkColorFilter* SkTableColorFilter::Create(const uint8_t table[256]) {
595    return new SkTable_ColorFilter(table, table, table, table);
596}
597
598SkColorFilter* SkTableColorFilter::CreateARGB(const uint8_t tableA[256],
599                                              const uint8_t tableR[256],
600                                              const uint8_t tableG[256],
601                                              const uint8_t tableB[256]) {
602    return new SkTable_ColorFilter(tableA, tableR, tableG, tableB);
603}
604
605SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkTableColorFilter)
606    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkTable_ColorFilter)
607SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
608