1/*
2 * Copyright 2014 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 "SkColorCubeFilter.h"
9#include "SkColorPriv.h"
10#include "SkOnce.h"
11#include "SkReadBuffer.h"
12#include "SkUnPreMultiply.h"
13#include "SkWriteBuffer.h"
14#if SK_SUPPORT_GPU
15#include "GrContext.h"
16#include "GrCoordTransform.h"
17#include "GrInvariantOutput.h"
18#include "GrTexturePriv.h"
19#include "SkGr.h"
20#include "gl/GrGLProcessor.h"
21#include "gl/builders/GrGLProgramBuilder.h"
22#endif
23
24///////////////////////////////////////////////////////////////////////////////
25namespace {
26
27int32_t SkNextColorCubeUniqueID() {
28    static int32_t gColorCubeUniqueID;
29    // do a loop in case our global wraps around, as we never want to return a 0
30    int32_t genID;
31    do {
32        genID = sk_atomic_inc(&gColorCubeUniqueID) + 1;
33    } while (0 == genID);
34    return genID;
35}
36
37} // end namespace
38
39static const int MIN_CUBE_SIZE = 4;
40static const int MAX_CUBE_SIZE = 64;
41
42static bool is_valid_3D_lut(SkData* cubeData, int cubeDimension) {
43    size_t minMemorySize = sizeof(uint8_t) * 4 * cubeDimension * cubeDimension * cubeDimension;
44    return (cubeDimension >= MIN_CUBE_SIZE) && (cubeDimension <= MAX_CUBE_SIZE) &&
45           (NULL != cubeData) && (cubeData->size() >= minMemorySize);
46}
47
48SkColorFilter* SkColorCubeFilter::Create(SkData* cubeData, int cubeDimension) {
49    if (!is_valid_3D_lut(cubeData, cubeDimension)) {
50        return NULL;
51    }
52
53    return SkNEW_ARGS(SkColorCubeFilter, (cubeData, cubeDimension));
54}
55
56SkColorCubeFilter::SkColorCubeFilter(SkData* cubeData, int cubeDimension)
57  : fCubeData(SkRef(cubeData))
58  , fUniqueID(SkNextColorCubeUniqueID())
59  , fCache(cubeDimension) {
60}
61
62uint32_t SkColorCubeFilter::getFlags() const {
63    return this->INHERITED::getFlags() | kAlphaUnchanged_Flag;
64}
65
66SkColorCubeFilter::ColorCubeProcesingCache::ColorCubeProcesingCache(int cubeDimension)
67  : fCubeDimension(cubeDimension)
68  , fLutsInited(false) {
69    fColorToIndex[0] = fColorToIndex[1] = NULL;
70    fColorToFactors[0] = fColorToFactors[1] = NULL;
71    fColorToScalar = NULL;
72}
73
74void SkColorCubeFilter::ColorCubeProcesingCache::getProcessingLuts(
75    const int* (*colorToIndex)[2], const SkScalar* (*colorToFactors)[2],
76    const SkScalar** colorToScalar) {
77    SkOnce(&fLutsInited, &fLutsMutex,
78           SkColorCubeFilter::ColorCubeProcesingCache::initProcessingLuts, this);
79    SkASSERT((fColorToIndex[0] != NULL) &&
80             (fColorToIndex[1] != NULL) &&
81             (fColorToFactors[0] != NULL) &&
82             (fColorToFactors[1] != NULL) &&
83             (fColorToScalar != NULL));
84    (*colorToIndex)[0] = fColorToIndex[0];
85    (*colorToIndex)[1] = fColorToIndex[1];
86    (*colorToFactors)[0] = fColorToFactors[0];
87    (*colorToFactors)[1] = fColorToFactors[1];
88    (*colorToScalar) = fColorToScalar;
89}
90
91void SkColorCubeFilter::ColorCubeProcesingCache::initProcessingLuts(
92    SkColorCubeFilter::ColorCubeProcesingCache* cache) {
93    static const SkScalar inv8bit = SkScalarInvert(SkIntToScalar(255));
94
95    // We need 256 int * 2 for fColorToIndex, so a total of 512 int.
96    // We need 256 SkScalar * 2 for fColorToFactors and 256 SkScalar
97    // for fColorToScalar, so a total of 768 SkScalar.
98    cache->fLutStorage.reset(512 * sizeof(int) + 768 * sizeof(SkScalar));
99    uint8_t* storage = (uint8_t*)cache->fLutStorage.get();
100    cache->fColorToIndex[0] = (int*)storage;
101    cache->fColorToIndex[1] = cache->fColorToIndex[0] + 256;
102    cache->fColorToFactors[0] = (SkScalar*)(storage + (512 * sizeof(int)));
103    cache->fColorToFactors[1] = cache->fColorToFactors[0] + 256;
104    cache->fColorToScalar = cache->fColorToFactors[1] + 256;
105
106    SkScalar size = SkIntToScalar(cache->fCubeDimension);
107    SkScalar scale = (size - SK_Scalar1) * inv8bit;
108
109    for (int i = 0; i < 256; ++i) {
110        SkScalar index = scale * i;
111        cache->fColorToIndex[0][i] = SkScalarFloorToInt(index);
112        cache->fColorToIndex[1][i] = cache->fColorToIndex[0][i] + 1;
113        cache->fColorToScalar[i] = inv8bit * i;
114        if (cache->fColorToIndex[1][i] < cache->fCubeDimension) {
115            cache->fColorToFactors[1][i] = index - SkIntToScalar(cache->fColorToIndex[0][i]);
116            cache->fColorToFactors[0][i] = SK_Scalar1 - cache->fColorToFactors[1][i];
117        } else {
118            cache->fColorToIndex[1][i] = cache->fColorToIndex[0][i];
119            cache->fColorToFactors[0][i] = SK_Scalar1;
120            cache->fColorToFactors[1][i] = 0;
121        }
122    }
123}
124
125void SkColorCubeFilter::filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const {
126    const int* colorToIndex[2];
127    const SkScalar* colorToFactors[2];
128    const SkScalar* colorToScalar;
129    fCache.getProcessingLuts(&colorToIndex, &colorToFactors, &colorToScalar);
130
131    const int dim = fCache.cubeDimension();
132    SkColor* colorCube = (SkColor*)fCubeData->data();
133    for (int i = 0; i < count; ++i) {
134        SkColor inputColor = SkUnPreMultiply::PMColorToColor(src[i]);
135        uint8_t r = SkColorGetR(inputColor);
136        uint8_t g = SkColorGetG(inputColor);
137        uint8_t b = SkColorGetB(inputColor);
138        uint8_t a = SkColorGetA(inputColor);
139        SkScalar rOut(0), gOut(0), bOut(0);
140        for (int x = 0; x < 2; ++x) {
141            for (int y = 0; y < 2; ++y) {
142                for (int z = 0; z < 2; ++z) {
143                    SkColor lutColor = colorCube[colorToIndex[x][r] +
144                                                (colorToIndex[y][g] +
145                                                 colorToIndex[z][b] * dim) * dim];
146                    SkScalar factor = colorToFactors[x][r] *
147                                      colorToFactors[y][g] *
148                                      colorToFactors[z][b];
149                    rOut += colorToScalar[SkColorGetR(lutColor)] * factor;
150                    gOut += colorToScalar[SkColorGetG(lutColor)] * factor;
151                    bOut += colorToScalar[SkColorGetB(lutColor)] * factor;
152                }
153            }
154        }
155        const SkScalar aOut = SkIntToScalar(a);
156        dst[i] = SkPackARGB32(a,
157            SkScalarRoundToInt(rOut * aOut),
158            SkScalarRoundToInt(gOut * aOut),
159            SkScalarRoundToInt(bOut * aOut));
160    }
161}
162
163SkFlattenable* SkColorCubeFilter::CreateProc(SkReadBuffer& buffer) {
164    int cubeDimension = buffer.readInt();
165    SkAutoDataUnref cubeData(buffer.readByteArrayAsData());
166    if (!buffer.validate(is_valid_3D_lut(cubeData, cubeDimension))) {
167        return NULL;
168    }
169    return Create(cubeData, cubeDimension);
170}
171
172void SkColorCubeFilter::flatten(SkWriteBuffer& buffer) const {
173    this->INHERITED::flatten(buffer);
174    buffer.writeInt(fCache.cubeDimension());
175    buffer.writeDataAsByteArray(fCubeData);
176}
177
178#ifndef SK_IGNORE_TO_STRING
179void SkColorCubeFilter::toString(SkString* str) const {
180    str->append("SkColorCubeFilter ");
181}
182#endif
183
184///////////////////////////////////////////////////////////////////////////////
185#if SK_SUPPORT_GPU
186
187class GrColorCubeEffect : public GrFragmentProcessor {
188public:
189    static GrFragmentProcessor* Create(GrTexture* colorCube) {
190        return (NULL != colorCube) ? SkNEW_ARGS(GrColorCubeEffect, (colorCube)) : NULL;
191    }
192
193    virtual ~GrColorCubeEffect();
194
195    const char* name() const override { return "ColorCube"; }
196
197    virtual void getGLProcessorKey(const GrGLSLCaps& caps,
198                                   GrProcessorKeyBuilder* b) const override;
199
200    GrGLFragmentProcessor* createGLInstance() const override;
201    int colorCubeSize() const { return fColorCubeAccess.getTexture()->width(); }
202
203
204    void onComputeInvariantOutput(GrInvariantOutput*) const override;
205
206    class GLProcessor : public GrGLFragmentProcessor {
207    public:
208        GLProcessor(const GrProcessor&);
209        virtual ~GLProcessor();
210
211        virtual void emitCode(GrGLFPBuilder*,
212                              const GrFragmentProcessor&,
213                              const char* outputColor,
214                              const char* inputColor,
215                              const TransformedCoordsArray&,
216                              const TextureSamplerArray&) override;
217
218        static inline void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder*);
219
220        void setData(const GrGLProgramDataManager&, const GrProcessor&) override;
221
222    private:
223        GrGLProgramDataManager::UniformHandle fColorCubeSizeUni;
224        GrGLProgramDataManager::UniformHandle fColorCubeInvSizeUni;
225
226        typedef GrGLFragmentProcessor INHERITED;
227    };
228
229private:
230    bool onIsEqual(const GrFragmentProcessor&) const override { return true; }
231
232    GrColorCubeEffect(GrTexture* colorCube);
233
234    GrTextureAccess     fColorCubeAccess;
235
236    typedef GrFragmentProcessor INHERITED;
237};
238
239///////////////////////////////////////////////////////////////////////////////
240
241GrColorCubeEffect::GrColorCubeEffect(GrTexture* colorCube)
242    : fColorCubeAccess(colorCube, "bgra", GrTextureParams::kBilerp_FilterMode) {
243    this->initClassID<GrColorCubeEffect>();
244    this->addTextureAccess(&fColorCubeAccess);
245}
246
247GrColorCubeEffect::~GrColorCubeEffect() {
248}
249
250void GrColorCubeEffect::getGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const {
251    GLProcessor::GenKey(*this, caps, b);
252}
253
254GrGLFragmentProcessor* GrColorCubeEffect::createGLInstance() const {
255    return SkNEW_ARGS(GLProcessor, (*this));
256}
257
258void GrColorCubeEffect::onComputeInvariantOutput(GrInvariantOutput* inout) const {
259    inout->setToUnknown(GrInvariantOutput::kWill_ReadInput);
260}
261
262///////////////////////////////////////////////////////////////////////////////
263
264GrColorCubeEffect::GLProcessor::GLProcessor(const GrProcessor&) {
265}
266
267GrColorCubeEffect::GLProcessor::~GLProcessor() {
268}
269
270void GrColorCubeEffect::GLProcessor::emitCode(GrGLFPBuilder* builder,
271                                              const GrFragmentProcessor&,
272                                              const char* outputColor,
273                                              const char* inputColor,
274                                              const TransformedCoordsArray& coords,
275                                              const TextureSamplerArray& samplers) {
276    if (NULL == inputColor) {
277        inputColor = "vec4(1)";
278    }
279
280    fColorCubeSizeUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
281                                            kFloat_GrSLType, kDefault_GrSLPrecision,
282                                            "Size");
283    const char* colorCubeSizeUni = builder->getUniformCStr(fColorCubeSizeUni);
284    fColorCubeInvSizeUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
285                                               kFloat_GrSLType, kDefault_GrSLPrecision,
286                                               "InvSize");
287    const char* colorCubeInvSizeUni = builder->getUniformCStr(fColorCubeInvSizeUni);
288
289    const char* nonZeroAlpha = "nonZeroAlpha";
290    const char* unPMColor = "unPMColor";
291    const char* cubeIdx = "cubeIdx";
292    const char* cCoords1 = "cCoords1";
293    const char* cCoords2 = "cCoords2";
294
295    // Note: if implemented using texture3D in OpenGL ES older than OpenGL ES 3.0,
296    //       the shader might need "#extension GL_OES_texture_3D : enable".
297
298    GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
299
300    // Unpremultiply color
301    fsBuilder->codeAppendf("\tfloat %s = max(%s.a, 0.00001);\n", nonZeroAlpha, inputColor);
302    fsBuilder->codeAppendf("\tvec4 %s = vec4(%s.rgb / %s, %s);\n",
303                           unPMColor, inputColor, nonZeroAlpha, nonZeroAlpha);
304
305    // Fit input color into the cube.
306    fsBuilder->codeAppendf(
307        "vec3 %s = vec3(%s.rg * vec2((%s - 1.0) * %s) + vec2(0.5 * %s), %s.b * (%s - 1.0));\n",
308        cubeIdx, unPMColor, colorCubeSizeUni, colorCubeInvSizeUni, colorCubeInvSizeUni,
309        unPMColor, colorCubeSizeUni);
310
311    // Compute y coord for for texture fetches.
312    fsBuilder->codeAppendf("vec2 %s = vec2(%s.r, (floor(%s.b) + %s.g) * %s);\n",
313                           cCoords1, cubeIdx, cubeIdx, cubeIdx, colorCubeInvSizeUni);
314    fsBuilder->codeAppendf("vec2 %s = vec2(%s.r, (ceil(%s.b) + %s.g) * %s);\n",
315                           cCoords2, cubeIdx, cubeIdx, cubeIdx, colorCubeInvSizeUni);
316
317    // Apply the cube.
318    fsBuilder->codeAppendf("%s = vec4(mix(", outputColor);
319    fsBuilder->appendTextureLookup(samplers[0], cCoords1);
320    fsBuilder->codeAppend(".rgb, ");
321    fsBuilder->appendTextureLookup(samplers[0], cCoords2);
322
323    // Premultiply color by alpha. Note that the input alpha is not modified by this shader.
324    fsBuilder->codeAppendf(".rgb, fract(%s.b)) * vec3(%s), %s.a);\n",
325                           cubeIdx, nonZeroAlpha, inputColor);
326}
327
328void GrColorCubeEffect::GLProcessor::setData(const GrGLProgramDataManager& pdman,
329                                             const GrProcessor& proc) {
330    const GrColorCubeEffect& colorCube = proc.cast<GrColorCubeEffect>();
331    SkScalar size = SkIntToScalar(colorCube.colorCubeSize());
332    pdman.set1f(fColorCubeSizeUni, SkScalarToFloat(size));
333    pdman.set1f(fColorCubeInvSizeUni, SkScalarToFloat(SkScalarInvert(size)));
334}
335
336void GrColorCubeEffect::GLProcessor::GenKey(const GrProcessor& proc,
337                                            const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
338}
339
340bool SkColorCubeFilter::asFragmentProcessors(GrContext* context,
341                                             SkTDArray<GrFragmentProcessor*>* array) const {
342    static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
343    GrUniqueKey key;
344    GrUniqueKey::Builder builder(&key, kDomain, 2);
345    builder[0] = fUniqueID;
346    builder[1] = fCache.cubeDimension();
347    builder.finish();
348
349    GrSurfaceDesc desc;
350    desc.fWidth = fCache.cubeDimension();
351    desc.fHeight = fCache.cubeDimension() * fCache.cubeDimension();
352    desc.fConfig = kRGBA_8888_GrPixelConfig;
353
354    SkAutoTUnref<GrTexture> textureCube(
355        context->textureProvider()->findAndRefTextureByUniqueKey(key));
356    if (!textureCube) {
357        textureCube.reset(context->textureProvider()->createTexture(
358            desc, true, fCubeData->data(), 0));
359        if (textureCube) {
360            context->textureProvider()->assignUniqueKeyToTexture(key, textureCube);
361        }
362    }
363
364    GrFragmentProcessor* frag = textureCube ? GrColorCubeEffect::Create(textureCube) : NULL;
365    if (frag) {
366        if (array) {
367            *array->append() = frag;
368        }
369        return true;
370    }
371    return false;
372}
373#endif
374