SkTableColorFilter.cpp revision f276ac5c16d39a2b877300d760041f0291bb5ec9
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 "gl/GrGLFragmentProcessor.h"
339#include "gl/builders/GrGLProgramBuilder.h"
340
341class ColorTableEffect : public GrFragmentProcessor {
342public:
343    static const GrFragmentProcessor* Create(GrContext* context, SkBitmap bitmap, unsigned flags);
344
345    virtual ~ColorTableEffect();
346
347    const char* name() const override { return "ColorTable"; }
348
349    const GrTextureStripAtlas* atlas() const { return fAtlas; }
350    int atlasRow() const { return fRow; }
351
352private:
353    GrGLFragmentProcessor* onCreateGLInstance() const override;
354
355    void onGetGLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
356
357    bool onIsEqual(const GrFragmentProcessor&) const override;
358
359    void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
360
361    ColorTableEffect(GrTexture* texture, GrTextureStripAtlas* atlas, int row, unsigned flags);
362
363    GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
364
365    GrTextureAccess         fTextureAccess;
366
367    // currently not used in shader code, just to assist onComputeInvariantOutput().
368    unsigned                fFlags;
369
370    GrTextureStripAtlas*    fAtlas;
371    int                     fRow;
372
373    typedef GrFragmentProcessor INHERITED;
374};
375
376class GLColorTableEffect : public GrGLFragmentProcessor {
377public:
378    GLColorTableEffect(const GrProcessor&);
379
380    virtual void emitCode(EmitArgs&) override;
381
382    static void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder* b) {}
383
384protected:
385    void onSetData(const GrGLProgramDataManager&, const GrProcessor&) override;
386
387private:
388    UniformHandle fRGBAYValuesUni;
389    typedef GrGLFragmentProcessor INHERITED;
390};
391
392GLColorTableEffect::GLColorTableEffect(const GrProcessor&) {
393}
394
395void GLColorTableEffect::onSetData(const GrGLProgramDataManager& pdm, const GrProcessor& proc) {
396    // The textures are organized in a strip where the rows are ordered a, r, g, b.
397    float rgbaYValues[4];
398    const ColorTableEffect& cte = proc.cast<ColorTableEffect>();
399    if (cte.atlas()) {
400        SkScalar yDelta = cte.atlas()->getNormalizedTexelHeight();
401        rgbaYValues[3] = cte.atlas()->getYOffset(cte.atlasRow()) + SK_ScalarHalf * yDelta;
402        rgbaYValues[0] = rgbaYValues[3] + yDelta;
403        rgbaYValues[1] = rgbaYValues[0] + yDelta;
404        rgbaYValues[2] = rgbaYValues[1] + yDelta;
405    } else {
406        rgbaYValues[3] = 0.125;
407        rgbaYValues[0] = 0.375;
408        rgbaYValues[1] = 0.625;
409        rgbaYValues[2] = 0.875;
410    }
411    pdm.set4fv(fRGBAYValuesUni, 1, rgbaYValues);
412}
413
414void GLColorTableEffect::emitCode(EmitArgs& args) {
415    const char* yoffsets;
416    fRGBAYValuesUni = args.fBuilder->addUniform(GrGLFPBuilder::kFragment_Visibility,
417                                          kVec4f_GrSLType, kDefault_GrSLPrecision,
418                                          "yoffsets", &yoffsets);
419    static const float kColorScaleFactor = 255.0f / 256.0f;
420    static const float kColorOffsetFactor = 1.0f / 512.0f;
421    GrGLFragmentBuilder* fsBuilder = args.fBuilder->getFragmentShaderBuilder();
422    if (nullptr == args.fInputColor) {
423        // the input color is solid white (all ones).
424        static const float kMaxValue = kColorScaleFactor + kColorOffsetFactor;
425        fsBuilder->codeAppendf("\t\tvec4 coord = vec4(%f, %f, %f, %f);\n",
426                               kMaxValue, kMaxValue, kMaxValue, kMaxValue);
427
428    } else {
429        fsBuilder->codeAppendf("\t\tfloat nonZeroAlpha = max(%s.a, .0001);\n", args.fInputColor);
430        fsBuilder->codeAppendf("\t\tvec4 coord = vec4(%s.rgb / nonZeroAlpha, nonZeroAlpha);\n",
431                               args.fInputColor);
432        fsBuilder->codeAppendf("\t\tcoord = coord * %f + vec4(%f, %f, %f, %f);\n",
433                              kColorScaleFactor,
434                              kColorOffsetFactor, kColorOffsetFactor,
435                              kColorOffsetFactor, kColorOffsetFactor);
436    }
437
438    SkString coord;
439
440    fsBuilder->codeAppendf("\t\t%s.a = ", args.fOutputColor);
441    coord.printf("vec2(coord.a, %s.a)", yoffsets);
442    fsBuilder->appendTextureLookup(args.fSamplers[0], coord.c_str());
443    fsBuilder->codeAppend(";\n");
444
445    fsBuilder->codeAppendf("\t\t%s.r = ", args.fOutputColor);
446    coord.printf("vec2(coord.r, %s.r)", yoffsets);
447    fsBuilder->appendTextureLookup(args.fSamplers[0], coord.c_str());
448    fsBuilder->codeAppend(";\n");
449
450    fsBuilder->codeAppendf("\t\t%s.g = ", args.fOutputColor);
451    coord.printf("vec2(coord.g, %s.g)", yoffsets);
452    fsBuilder->appendTextureLookup(args.fSamplers[0], coord.c_str());
453    fsBuilder->codeAppend(";\n");
454
455    fsBuilder->codeAppendf("\t\t%s.b = ", args.fOutputColor);
456    coord.printf("vec2(coord.b, %s.b)", yoffsets);
457    fsBuilder->appendTextureLookup(args.fSamplers[0], coord.c_str());
458    fsBuilder->codeAppend(";\n");
459
460    fsBuilder->codeAppendf("\t\t%s.rgb *= %s.a;\n", args.fOutputColor, args.fOutputColor);
461}
462
463///////////////////////////////////////////////////////////////////////////////
464const GrFragmentProcessor* ColorTableEffect::Create(GrContext* context, SkBitmap bitmap,
465                                                    unsigned flags) {
466
467    GrTextureStripAtlas::Desc desc;
468    desc.fWidth  = bitmap.width();
469    desc.fHeight = 128;
470    desc.fRowHeight = bitmap.height();
471    desc.fContext = context;
472    desc.fConfig = SkImageInfo2GrPixelConfig(bitmap.info());
473    GrTextureStripAtlas* atlas = GrTextureStripAtlas::GetAtlas(desc);
474    int row = atlas->lockRow(bitmap);
475    SkAutoTUnref<GrTexture> texture;
476    if (-1 == row) {
477        atlas = nullptr;
478        // Passing params=nullptr because this effect does no tiling or filtering.
479        texture.reset(GrRefCachedBitmapTexture(context, bitmap, nullptr));
480    } else {
481        texture.reset(SkRef(atlas->getTexture()));
482    }
483
484    return new ColorTableEffect(texture, atlas, row, flags);
485}
486
487ColorTableEffect::ColorTableEffect(GrTexture* texture, GrTextureStripAtlas* atlas, int row,
488                                   unsigned flags)
489    : fTextureAccess(texture, "a")
490    , fFlags(flags)
491    , fAtlas(atlas)
492    , fRow(row) {
493    this->initClassID<ColorTableEffect>();
494    this->addTextureAccess(&fTextureAccess);
495}
496
497ColorTableEffect::~ColorTableEffect() {
498    if (fAtlas) {
499        fAtlas->unlockRow(fRow);
500    }
501}
502
503void ColorTableEffect::onGetGLProcessorKey(const GrGLSLCaps& caps,
504                                         GrProcessorKeyBuilder* b) const {
505    GLColorTableEffect::GenKey(*this, caps, b);
506}
507
508GrGLFragmentProcessor* ColorTableEffect::onCreateGLInstance() const {
509    return new GLColorTableEffect(*this);
510}
511
512bool ColorTableEffect::onIsEqual(const GrFragmentProcessor& other) const {
513    // For non-atlased instances, the texture (compared by base class) is sufficient to
514    // differentiate different tables. For atlased instances we ensure they are using the
515    // same row.
516    const ColorTableEffect& that = other.cast<ColorTableEffect>();
517    SkASSERT(SkToBool(fAtlas) == SkToBool(that.fAtlas));
518    // Ok to always do this comparison since both would be -1 if non-atlased.
519    return fRow == that.fRow;
520}
521
522void ColorTableEffect::onComputeInvariantOutput(GrInvariantOutput* inout) const {
523    // If we kept the table in the effect then we could actually run known inputs through the
524    // table.
525    GrColorComponentFlags invalidateFlags = kNone_GrColorComponentFlags;
526    if (fFlags & SkTable_ColorFilter::kR_Flag) {
527        invalidateFlags |= kR_GrColorComponentFlag;
528    }
529    if (fFlags & SkTable_ColorFilter::kG_Flag) {
530        invalidateFlags |= kG_GrColorComponentFlag;
531    }
532    if (fFlags & SkTable_ColorFilter::kB_Flag) {
533        invalidateFlags |= kB_GrColorComponentFlag;
534    }
535    if (fFlags & SkTable_ColorFilter::kA_Flag) {
536        invalidateFlags |= kA_GrColorComponentFlag;
537    }
538    inout->invalidateComponents(invalidateFlags, GrInvariantOutput::kWill_ReadInput);
539}
540
541///////////////////////////////////////////////////////////////////////////////
542
543GR_DEFINE_FRAGMENT_PROCESSOR_TEST(ColorTableEffect);
544
545const GrFragmentProcessor* ColorTableEffect::TestCreate(GrProcessorTestData* d) {
546    int flags = 0;
547    uint8_t luts[256][4];
548    do {
549        for (int i = 0; i < 4; ++i) {
550            flags |= d->fRandom->nextBool() ? (1  << i): 0;
551        }
552    } while (!flags);
553    for (int i = 0; i < 4; ++i) {
554        if (flags & (1 << i)) {
555            for (int j = 0; j < 256; ++j) {
556                luts[j][i] = SkToU8(d->fRandom->nextBits(8));
557            }
558        }
559    }
560    SkAutoTUnref<SkColorFilter> filter(SkTableColorFilter::CreateARGB(
561        (flags & (1 << 0)) ? luts[0] : nullptr,
562        (flags & (1 << 1)) ? luts[1] : nullptr,
563        (flags & (1 << 2)) ? luts[2] : nullptr,
564        (flags & (1 << 3)) ? luts[3] : nullptr
565    ));
566
567    const GrFragmentProcessor* fp = filter->asFragmentProcessor(d->fContext);
568    SkASSERT(fp);
569    return fp;
570}
571
572const GrFragmentProcessor* SkTable_ColorFilter::asFragmentProcessor(GrContext* context) const {
573    SkBitmap bitmap;
574    this->asComponentTable(&bitmap);
575
576    return ColorTableEffect::Create(context, bitmap, fFlags);
577}
578
579#endif // SK_SUPPORT_GPU
580
581///////////////////////////////////////////////////////////////////////////////
582
583#ifdef SK_CPU_BENDIAN
584#else
585    #define SK_A32_INDEX    (3 - (SK_A32_SHIFT >> 3))
586    #define SK_R32_INDEX    (3 - (SK_R32_SHIFT >> 3))
587    #define SK_G32_INDEX    (3 - (SK_G32_SHIFT >> 3))
588    #define SK_B32_INDEX    (3 - (SK_B32_SHIFT >> 3))
589#endif
590
591///////////////////////////////////////////////////////////////////////////////
592
593SkColorFilter* SkTableColorFilter::Create(const uint8_t table[256]) {
594    return new SkTable_ColorFilter(table, table, table, table);
595}
596
597SkColorFilter* SkTableColorFilter::CreateARGB(const uint8_t tableA[256],
598                                              const uint8_t tableR[256],
599                                              const uint8_t tableG[256],
600                                              const uint8_t tableB[256]) {
601    return new SkTable_ColorFilter(tableA, tableR, tableG, tableB);
602}
603
604SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkTableColorFilter)
605    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkTable_ColorFilter)
606SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
607