Adding 3D lut color filter
authorsugoi <sugoi@chromium.org>
Thu, 9 Oct 2014 12:27:23 +0000 (05:27 -0700)
committerCommit bot <commit-bot@chromium.org>
Thu, 9 Oct 2014 12:27:23 +0000 (05:27 -0700)
Included in this cl is support for 3D textures.

BUG=skia:

Review URL: https://codereview.chromium.org/580863004

bench/ColorCubeBench.cpp [new file with mode: 0644]
gm/colorcube.cpp [new file with mode: 0644]
gyp/bench.gypi
gyp/effects.gypi
gyp/gmslides.gypi
include/effects/SkColorCubeFilter.h [new file with mode: 0644]
src/effects/SkColorCubeFilter.cpp [new file with mode: 0644]
src/ports/SkGlobalInitialization_chromium.cpp
src/ports/SkGlobalInitialization_default.cpp

diff --git a/bench/ColorCubeBench.cpp b/bench/ColorCubeBench.cpp
new file mode 100644 (file)
index 0000000..16ed4ce
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "Benchmark.h"
+#include "SkCanvas.h"
+#include "SkColorCubeFilter.h"
+#include "SkGradientShader.h"
+
+class ColorCubeBench : public Benchmark {
+    SkISize fSize;
+    int fCubeDimension;
+    SkData* fCubeData;
+    SkBitmap fBitmap;
+
+public:
+    ColorCubeBench()
+     : fCubeDimension(0)
+     , fCubeData(NULL) {
+        fSize = SkISize::Make(2880, 1800); // 2014 Macbook Pro resolution
+    }
+
+    ~ColorCubeBench() {
+        SkSafeUnref(fCubeData);
+    }
+
+protected:
+    virtual const char* onGetName() SK_OVERRIDE {
+        return "colorcube";
+    }
+
+    virtual void onPreDraw() SK_OVERRIDE {
+        if (!SkToBool(fCubeData)) {
+            this->makeCubeData();
+            this->make_bitmap();
+        }
+    }
+
+    virtual void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+        this->test(loops, canvas);
+    }
+
+    virtual SkIPoint onGetSize() SK_OVERRIDE {
+        return SkIPoint::Make(fSize.width(), fSize.height());
+    }
+
+private:
+    static SkShader* MakeLinear(const SkISize& size) {
+        const SkPoint pts[2] = {
+                { 0, 0 },
+                { SkIntToScalar(size.width()), SkIntToScalar(size.height()) }
+            };
+        static const SkColor colors[] = { SK_ColorYELLOW, SK_ColorBLUE };
+        return SkGradientShader::CreateLinear(
+            pts, colors, NULL, 2, SkShader::kRepeat_TileMode, 0, &SkMatrix::I());
+    }
+
+    void make_bitmap() {
+        fBitmap.allocN32Pixels(fSize.width(), fSize.height());
+        SkCanvas canvas(fBitmap);
+        canvas.clear(0x00000000);
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        SkShader* shader = MakeLinear(fSize);
+        paint.setShader(shader);
+        SkRect r = { 0, 0, SkIntToScalar(fSize.width()), SkIntToScalar(fSize.height()) };
+        canvas.drawRect(r, paint);
+        shader->unref();
+    }
+
+    void makeCubeData() {
+        fCubeDimension = 32;
+        fCubeData = SkData::NewUninitialized(sizeof(SkColor) *
+            fCubeDimension * fCubeDimension * fCubeDimension);
+        SkColor* pixels = (SkColor*)(fCubeData->writable_data());
+        SkAutoMalloc lutMemory(fCubeDimension);
+        uint8_t* lut = (uint8_t*)lutMemory.get();
+        const int maxIndex = fCubeDimension - 1;
+        for (int i = 0; i < fCubeDimension; ++i) {
+            // Make an invert lut, but the content of
+            // the lut shouldn't affect performance.
+            lut[i] = ((maxIndex - i) * 255) / maxIndex;
+        }
+        for (int r = 0; r < fCubeDimension; ++r) {
+            for (int g = 0; g < fCubeDimension; ++g) {
+                for (int b = 0; b < fCubeDimension; ++b) {
+                    pixels[(fCubeDimension * ((fCubeDimension * b) + g)) + r] =
+                        SkColorSetARGB(0xFF, lut[r], lut[g], lut[b]);
+                }
+            }
+        }
+    }
+
+    void test(const int loops, SkCanvas* canvas) {
+        SkAutoTUnref<SkColorFilter> colorCube(
+            SkColorCubeFilter::Create(fCubeData, fCubeDimension));
+        SkPaint paint;
+        paint.setColorFilter(colorCube);
+
+        for (int i = 0; i < loops; i++) {
+            canvas->drawBitmap(fBitmap, 0, 0, &paint);
+        }
+    }
+
+    typedef Benchmark INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+DEF_BENCH( return new ColorCubeBench(); )
diff --git a/gm/colorcube.cpp b/gm/colorcube.cpp
new file mode 100644 (file)
index 0000000..59463fc
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "gm.h"
+#include "SkColorCubeFilter.h"
+#include "SkBitmapSource.h"
+#include "SkData.h"
+#include "SkGradientShader.h"
+
+namespace skiagm {
+
+static SkShader* MakeLinear() {
+    static const SkPoint pts[2] = {
+            { 0, 0 },
+            { SkIntToScalar(80), SkIntToScalar(80) }
+        };
+    static const SkColor colors[] = { SK_ColorYELLOW, SK_ColorBLUE };
+    return SkGradientShader::CreateLinear(
+        pts, colors, NULL, 2, SkShader::kRepeat_TileMode, 0, &SkMatrix::I());
+}
+
+class ColorCubeGM : public GM {
+public:
+    ColorCubeGM()
+    : fInitialized(false)
+    , f3DLut4(NULL)
+    , f3DLut8(NULL)
+    , f3DLut16(NULL)
+    , f3DLut32(NULL)
+    , f3DLut64(NULL)
+    {
+        this->setBGColor(0xFF000000);
+    }
+
+    ~ColorCubeGM() {
+        SkSafeUnref(f3DLut4);
+        SkSafeUnref(f3DLut8);
+        SkSafeUnref(f3DLut16);
+        SkSafeUnref(f3DLut32);
+        SkSafeUnref(f3DLut64);
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("colorcube");
+    }
+
+    void make_3Dluts() {
+        make_3Dlut(&f3DLut4, 4, true, false, false);
+        make_3Dlut(&f3DLut8, 8, false, true, false);
+        make_3Dlut(&f3DLut16, 16, false, true, true);
+        make_3Dlut(&f3DLut32, 32, true, true, false);
+        make_3Dlut(&f3DLut64, 64, true, false, true);
+    }
+
+    void make_bitmap() {
+        fBitmap.allocN32Pixels(80, 80);
+        SkCanvas canvas(fBitmap);
+        canvas.clear(0x00000000);
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        SkShader* shader = MakeLinear();
+        paint.setShader(shader);
+        SkRect r = { 0, 0, SkIntToScalar(80), SkIntToScalar(80) };
+        canvas.drawRect(r, paint);
+        shader->unref();
+    }
+
+    void make_3Dlut(SkData** data, int size, bool invR, bool invG, bool invB) {
+        *data = SkData::NewUninitialized(sizeof(SkColor) * size * size * size);
+        SkColor* pixels = (SkColor*)((*data)->writable_data());
+        SkAutoMalloc lutMemory(size);
+        SkAutoMalloc invLutMemory(size);
+        uint8_t* lut = (uint8_t*)lutMemory.get();
+        uint8_t* invLut = (uint8_t*)invLutMemory.get();
+        const int maxIndex = size - 1;
+        for (int i = 0; i < size; i++) {
+            lut[i] = (i * 255) / maxIndex;
+            invLut[i] = ((maxIndex - i) * 255) / maxIndex;
+        }
+        for (int r = 0; r < size; ++r) {
+            for (int g = 0; g < size; ++g) {
+                for (int b = 0; b < size; ++b) {
+                    pixels[(size * ((size * b) + g)) + r] = SkColorSetARGB(0xFF,
+                            invR ? invLut[r] : lut[r],
+                            invG ? invLut[g] : lut[g],
+                            invB ? invLut[b] : lut[b]);
+                }
+            }
+        }
+    }
+
+    virtual SkISize onISize() {
+        return SkISize::Make(500, 100);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        if (!fInitialized) {
+            this->make_bitmap();
+            this->make_3Dluts();
+            fInitialized = true;
+        }
+        canvas->clear(0x00000000);
+        SkPaint paint;
+        paint.setColorFilter(SkColorCubeFilter::Create(f3DLut4, 4))->unref();
+        canvas->drawBitmap(fBitmap, 10, 10, &paint);
+
+        paint.setColorFilter(SkColorCubeFilter::Create(f3DLut8, 8))->unref();
+        canvas->drawBitmap(fBitmap, 110, 10, &paint);
+
+        paint.setColorFilter(SkColorCubeFilter::Create(f3DLut16, 16))->unref();
+        canvas->drawBitmap(fBitmap, 210, 10, &paint);
+
+        paint.setColorFilter(SkColorCubeFilter::Create(f3DLut32, 32))->unref();
+        canvas->drawBitmap(fBitmap, 310, 10, &paint);
+
+        paint.setColorFilter(SkColorCubeFilter::Create(f3DLut64, 64))->unref();
+        canvas->drawBitmap(fBitmap, 410, 10, &paint);
+    }
+
+private:
+    typedef GM INHERITED;
+    bool fInitialized;
+    SkBitmap fBitmap;
+    SkData* f3DLut4;
+    SkData* f3DLut8;
+    SkData* f3DLut16;
+    SkData* f3DLut32;
+    SkData* f3DLut64;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new ColorCubeGM; }
+static GMRegistry reg(MyFactory);
+
+}
index adb98a1..c2c8ec9 100644 (file)
@@ -35,6 +35,7 @@
     '../bench/ChecksumBench.cpp',
     '../bench/ChromeBench.cpp',
     '../bench/CmapBench.cpp',
+    '../bench/ColorCubeBench.cpp',
     '../bench/ColorFilterBench.cpp',
     '../bench/ColorPrivBench.cpp',
     '../bench/CoverageBench.cpp',
index da2f5db..c82d886 100644 (file)
@@ -18,6 +18,7 @@
     '<(skia_src_path)/effects/SkBlurMask.h',
     '<(skia_src_path)/effects/SkBlurImageFilter.cpp',
     '<(skia_src_path)/effects/SkBlurMaskFilter.cpp',
+    '<(skia_src_path)/effects/SkColorCubeFilter.cpp',
     '<(skia_src_path)/effects/SkColorFilters.cpp',
     '<(skia_src_path)/effects/SkColorFilterImageFilter.cpp',
     '<(skia_src_path)/effects/SkColorMatrix.cpp',
@@ -88,6 +89,7 @@
     '<(skia_include_path)/effects/SkBlurDrawLooper.h',
     '<(skia_include_path)/effects/SkBlurImageFilter.h',
     '<(skia_include_path)/effects/SkBlurMaskFilter.h',
+    '<(skia_include_path)/effects/SkColorCubeFilter.h',
     '<(skia_include_path)/effects/SkColorMatrix.h',
     '<(skia_include_path)/effects/SkColorMatrixFilter.h',
     '<(skia_include_path)/effects/SkColorFilterImageFilter.h',
index 557cc4a..58bf177 100644 (file)
@@ -42,6 +42,7 @@
         '../gm/circularclips.cpp',
         '../gm/clip_strokerect.cpp',
         '../gm/clippedbitmapshaders.cpp',
+        '../gm/colorcube.cpp',
         '../gm/coloremoji.cpp',
         '../gm/colorfilterimagefilter.cpp',
         '../gm/colorfilters.cpp',
diff --git a/include/effects/SkColorCubeFilter.h b/include/effects/SkColorCubeFilter.h
new file mode 100644 (file)
index 0000000..2cfee45
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkColorCubeFilter_DEFINED
+#define SkColorCubeFilter_DEFINED
+
+#include "SkColorFilter.h"
+#include "SkData.h"
+
+class SK_API SkColorCubeFilter : public SkColorFilter {
+public:
+    /** cubeData must containt a 3D data in the form of cube of the size:
+     *  cubeDimension * cubeDimension * cubeDimension * sizeof(SkColor)
+     *  This cube contains a transform where (x,y,z) maps to the (r,g,b).
+     *  The alpha components of the colors are ignored (treated as 0xFF).
+     */
+    static SkColorFilter* Create(SkData* cubeData, int cubeDimension);
+
+    virtual void filterSpan(const SkPMColor src[], int count, SkPMColor[]) const SK_OVERRIDE;
+    virtual uint32_t getFlags() const SK_OVERRIDE;
+
+#if SK_SUPPORT_GPU
+   virtual GrFragmentProcessor* asFragmentProcessor(GrContext*) const SK_OVERRIDE;
+#endif
+
+    SK_TO_STRING_OVERRIDE()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkColorCubeFilter)
+
+protected:
+    SkColorCubeFilter(SkData* cubeData, int cubeDimension);
+#ifdef SK_SUPPORT_LEGACY_DEEPFLATTENING
+    SkColorCubeFilter(SkReadBuffer& buffer);
+#endif
+    virtual void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+
+private:
+    /** The cache is initialized on-demand when getProcessingLuts is called.
+     */
+    class ColorCubeProcesingCache {
+    public:
+        ColorCubeProcesingCache(int cubeDimension);
+
+        void getProcessingLuts(const int* (*colorToIndex)[2],
+                               const SkScalar* (*colorToFactors)[2],
+                               const SkScalar** colorToScalar);
+
+        int cubeDimension() const { return fCubeDimension; }
+
+    private:
+        // Working pointers. If any of these is NULL,
+        // we need to recompute the corresponding cache values.
+        int* fColorToIndex[2];
+        SkScalar* fColorToFactors[2];
+        SkScalar* fColorToScalar;
+
+        SkAutoMalloc fLutStorage;
+
+        const int fCubeDimension;
+
+        // Make sure we only initialize the caches once.
+        SkMutex fLutsMutex;
+        bool fLutsInited;
+
+        static void initProcessingLuts(ColorCubeProcesingCache* cache);
+    };
+
+    SkAutoDataUnref fCubeData;
+    int32_t fUniqueID;
+
+    mutable ColorCubeProcesingCache fCache;
+
+    typedef SkColorFilter INHERITED;
+};
+
+#endif
diff --git a/src/effects/SkColorCubeFilter.cpp b/src/effects/SkColorCubeFilter.cpp
new file mode 100644 (file)
index 0000000..cf3126b
--- /dev/null
@@ -0,0 +1,376 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkColorCubeFilter.h"
+#include "SkColorPriv.h"
+#include "SkOnce.h"
+#include "SkReadBuffer.h"
+#include "SkUnPreMultiply.h"
+#include "SkWriteBuffer.h"
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#include "GrCoordTransform.h"
+#include "gl/GrGLProcessor.h"
+#include "gl/builders/GrGLProgramBuilder.h"
+#include "GrTBackendProcessorFactory.h"
+#include "GrTexturePriv.h"
+#include "SkGr.h"
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+namespace {
+
+int32_t SkNextColorProfileUniqueID() {
+    static int32_t gColorProfileUniqueID;
+    // do a loop in case our global wraps around, as we never want to return a 0
+    int32_t genID;
+    do {
+        genID = sk_atomic_inc(&gColorProfileUniqueID) + 1;
+    } while (0 == genID);
+    return genID;
+}
+
+} // end namespace
+
+static const int MIN_CUBE_SIZE = 4;
+static const int MAX_CUBE_SIZE = 64;
+
+static bool is_valid_3D_lut(SkData* cubeData, int cubeDimension) {
+    size_t minMemorySize = sizeof(uint8_t) * 4 * cubeDimension * cubeDimension * cubeDimension;
+    return (cubeDimension >= MIN_CUBE_SIZE) && (cubeDimension <= MAX_CUBE_SIZE) &&
+           (NULL != cubeData) && (cubeData->size() >= minMemorySize);
+}
+
+SkColorFilter* SkColorCubeFilter::Create(SkData* cubeData, int cubeDimension) {
+    if (!is_valid_3D_lut(cubeData, cubeDimension)) {
+        return NULL;
+    }
+
+    return SkNEW_ARGS(SkColorCubeFilter, (cubeData, cubeDimension));
+}
+
+SkColorCubeFilter::SkColorCubeFilter(SkData* cubeData, int cubeDimension)
+  : fCubeData(SkRef(cubeData))
+  , fUniqueID(SkNextColorProfileUniqueID())
+  , fCache(cubeDimension) {
+}
+
+uint32_t SkColorCubeFilter::getFlags() const {
+    return this->INHERITED::getFlags() | kAlphaUnchanged_Flag;
+}
+
+SkColorCubeFilter::ColorCubeProcesingCache::ColorCubeProcesingCache(int cubeDimension)
+  : fCubeDimension(cubeDimension)
+  , fLutsInited(false) {
+    fColorToIndex[0] = fColorToIndex[1] = NULL;
+    fColorToFactors[0] = fColorToFactors[1] = NULL;
+    fColorToScalar = NULL;
+}
+
+void SkColorCubeFilter::ColorCubeProcesingCache::getProcessingLuts(
+    const int* (*colorToIndex)[2], const SkScalar* (*colorToFactors)[2],
+    const SkScalar** colorToScalar) {
+    SkOnce(&fLutsInited, &fLutsMutex,
+           SkColorCubeFilter::ColorCubeProcesingCache::initProcessingLuts, this);
+    SkASSERT((fColorToIndex[0] != NULL) &&
+             (fColorToIndex[1] != NULL) &&
+             (fColorToFactors[0] != NULL) &&
+             (fColorToFactors[1] != NULL) &&
+             (fColorToScalar != NULL));
+    (*colorToIndex)[0] = fColorToIndex[0];
+    (*colorToIndex)[1] = fColorToIndex[1];
+    (*colorToFactors)[0] = fColorToFactors[0];
+    (*colorToFactors)[1] = fColorToFactors[1];
+    (*colorToScalar) = fColorToScalar;
+}
+
+void SkColorCubeFilter::ColorCubeProcesingCache::initProcessingLuts(
+    SkColorCubeFilter::ColorCubeProcesingCache* cache) {
+    static const SkScalar inv8bit = SkScalarInvert(SkIntToScalar(255));
+
+    // We need 256 int * 2 for fColorToIndex, so a total of 512 int.
+    // We need 256 SkScalar * 2 for fColorToFactors and 256 SkScalar
+    // for fColorToScalar, so a total of 768 SkScalar.
+    cache->fLutStorage.reset(512 * sizeof(int) + 768 * sizeof(SkScalar));
+    uint8_t* storage = (uint8_t*)cache->fLutStorage.get();
+    cache->fColorToIndex[0] = (int*)storage;
+    cache->fColorToIndex[1] = cache->fColorToIndex[0] + 256;
+    cache->fColorToFactors[0] = (SkScalar*)(storage + (512 * sizeof(int)));
+    cache->fColorToFactors[1] = cache->fColorToFactors[0] + 256;
+    cache->fColorToScalar = cache->fColorToFactors[1] + 256;
+
+    SkScalar size = SkIntToScalar(cache->fCubeDimension);
+    SkScalar scale = (size - SK_Scalar1) * inv8bit;
+
+    for (int i = 0; i < 256; ++i) {
+        SkScalar index = scale * i;
+        cache->fColorToIndex[0][i] = SkScalarFloorToInt(index);
+        cache->fColorToIndex[1][i] = cache->fColorToIndex[0][i] + 1;
+        cache->fColorToScalar[i] = inv8bit * i;
+        if (cache->fColorToIndex[1][i] < cache->fCubeDimension) {
+            cache->fColorToFactors[1][i] = index - SkIntToScalar(cache->fColorToIndex[0][i]);
+            cache->fColorToFactors[0][i] = SK_Scalar1 - cache->fColorToFactors[1][i];
+        } else {
+            cache->fColorToIndex[1][i] = cache->fColorToIndex[0][i];
+            cache->fColorToFactors[0][i] = SK_Scalar1;
+            cache->fColorToFactors[1][i] = 0;
+        }
+    }
+}
+
+void SkColorCubeFilter::filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const {
+    const int* colorToIndex[2];
+    const SkScalar* colorToFactors[2];
+    const SkScalar* colorToScalar;
+    fCache.getProcessingLuts(&colorToIndex, &colorToFactors, &colorToScalar);
+
+    const int dim = fCache.cubeDimension();
+    SkColor* colorCube = (SkColor*)fCubeData->data();
+    for (int i = 0; i < count; ++i) {
+        SkColor inputColor = SkUnPreMultiply::PMColorToColor(src[i]);
+        uint8_t r = SkColorGetR(inputColor);
+        uint8_t g = SkColorGetG(inputColor);
+        uint8_t b = SkColorGetB(inputColor);
+        uint8_t a = SkColorGetA(inputColor);
+        SkScalar rOut(0), gOut(0), bOut(0);
+        for (int x = 0; x < 2; ++x) {
+            for (int y = 0; y < 2; ++y) {
+                for (int z = 0; z < 2; ++z) {
+                    SkColor lutColor = colorCube[colorToIndex[x][r] +
+                                                (colorToIndex[y][g] +
+                                                 colorToIndex[z][b] * dim) * dim];
+                    SkScalar factor = colorToFactors[x][r] *
+                                      colorToFactors[y][g] *
+                                      colorToFactors[z][b];
+                    rOut += colorToScalar[SkColorGetR(lutColor)] * factor;
+                    gOut += colorToScalar[SkColorGetG(lutColor)] * factor;
+                    bOut += colorToScalar[SkColorGetB(lutColor)] * factor;
+                }
+            }
+        }
+        const SkScalar aOut = SkIntToScalar(a);
+        dst[i] = SkPackARGB32(a,
+            SkScalarRoundToInt(rOut * aOut),
+            SkScalarRoundToInt(gOut * aOut),
+            SkScalarRoundToInt(bOut * aOut));
+    }
+}
+
+#ifdef SK_SUPPORT_LEGACY_DEEPFLATTENING
+SkColorCubeFilter::SkColorCubeFilter(SkReadBuffer& buffer)
+  : fCache(buffer.readInt()) {
+    fCubeData.reset(buffer.readByteArrayAsData());
+    buffer.validate(is_valid_3D_lut(fCubeData, fCache.cubeDimension()));
+    fUniqueID = SkNextColorProfileUniqueID();
+}
+#endif
+
+SkFlattenable* SkColorCubeFilter::CreateProc(SkReadBuffer& buffer) {
+    int cubeDimension = buffer.readInt();
+    SkData* cubeData = buffer.readByteArrayAsData();
+    if (!buffer.validate(is_valid_3D_lut(cubeData, cubeDimension))) {
+        SkSafeUnref(cubeData);
+        return NULL;
+    }
+    return Create(cubeData, cubeDimension);
+}
+
+void SkColorCubeFilter::flatten(SkWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writeInt(fCache.cubeDimension());
+    buffer.writeDataAsByteArray(fCubeData);
+}
+
+#ifndef SK_IGNORE_TO_STRING
+void SkColorCubeFilter::toString(SkString* str) const {
+    str->append("SkColorCubeFilter ");
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+#if SK_SUPPORT_GPU
+class GrColorProfileEffect : public GrFragmentProcessor {
+public:
+    static GrFragmentProcessor* Create(GrTexture* colorCube) {
+        return (NULL != colorCube) ? SkNEW_ARGS(GrColorProfileEffect, (colorCube)) : NULL;
+    }
+
+    virtual ~GrColorProfileEffect();
+
+    virtual const GrBackendFragmentProcessorFactory& getFactory() const SK_OVERRIDE;
+    int colorCubeSize() const { return fColorCubeAccess.getTexture()->width(); }
+
+    static const char* Name() { return "ColorProfile"; }
+
+    virtual void onComputeInvariantOutput(GrProcessor::InvariantOutput*) const SK_OVERRIDE;
+
+    class GLProcessor : public GrGLFragmentProcessor {
+    public:
+        GLProcessor(const GrBackendProcessorFactory& factory, const GrProcessor&);
+        virtual ~GLProcessor();
+
+        virtual void emitCode(GrGLProgramBuilder*,
+                              const GrFragmentProcessor&,
+                              const GrProcessorKey&,
+                              const char* outputColor,
+                              const char* inputColor,
+                              const TransformedCoordsArray&,
+                              const TextureSamplerArray&) SK_OVERRIDE;
+
+        static inline void GenKey(const GrProcessor&, const GrGLCaps&, GrProcessorKeyBuilder*);
+
+        virtual void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
+
+    private:
+        GrGLProgramDataManager::UniformHandle fColorCubeSizeUni;
+        GrGLProgramDataManager::UniformHandle fColorCubeInvSizeUni;
+
+        typedef GrGLFragmentProcessor INHERITED;
+    };
+
+private:
+    virtual bool onIsEqual(const GrProcessor&) const SK_OVERRIDE;
+
+    GrColorProfileEffect(GrTexture* colorCube);
+
+    GrCoordTransform    fColorCubeTransform;
+    GrTextureAccess     fColorCubeAccess;
+
+    typedef GrFragmentProcessor INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrColorProfileEffect::GrColorProfileEffect(GrTexture* colorCube)
+    : fColorCubeTransform(kLocal_GrCoordSet, colorCube)
+    , fColorCubeAccess(colorCube, "bgra", GrTextureParams::kBilerp_FilterMode) {
+    this->addCoordTransform(&fColorCubeTransform);
+    this->addTextureAccess(&fColorCubeAccess);
+}
+
+GrColorProfileEffect::~GrColorProfileEffect() {
+}
+
+bool GrColorProfileEffect::onIsEqual(const GrProcessor& sBase) const {
+    const GrColorProfileEffect& s = sBase.cast<GrColorProfileEffect>();
+    return fColorCubeAccess.getTexture() == s.fColorCubeAccess.getTexture();
+}
+
+const GrBackendFragmentProcessorFactory& GrColorProfileEffect::getFactory() const {
+    return GrTBackendFragmentProcessorFactory<GrColorProfileEffect>::getInstance();
+}
+
+void GrColorProfileEffect::onComputeInvariantOutput(InvariantOutput* inout) const {
+    inout->fValidFlags = 0;
+    inout->fIsSingleComponent = false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrColorProfileEffect::GLProcessor::GLProcessor(const GrBackendProcessorFactory& factory,
+                                               const GrProcessor&)
+    : INHERITED(factory) {
+}
+
+GrColorProfileEffect::GLProcessor::~GLProcessor() {
+}
+
+void GrColorProfileEffect::GLProcessor::emitCode(GrGLProgramBuilder* builder,
+                                                 const GrFragmentProcessor&,
+                                                 const GrProcessorKey&,
+                                                 const char* outputColor,
+                                                 const char* inputColor,
+                                                 const TransformedCoordsArray& coords,
+                                                 const TextureSamplerArray& samplers) {
+    if (NULL == inputColor) {
+        inputColor = "vec4(1)";
+    }
+
+    fColorCubeSizeUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
+                                            kFloat_GrSLType, "Size");
+    const char* colorCubeSizeUni = builder->getUniformCStr(fColorCubeSizeUni);
+    fColorCubeInvSizeUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
+                                               kFloat_GrSLType, "InvSize");
+    const char* colorCubeInvSizeUni = builder->getUniformCStr(fColorCubeInvSizeUni);
+
+    const char* nonZeroAlpha = "nonZeroAlpha";
+    const char* unPMColor = "unPMColor";
+    const char* cubeIdx = "cubeIdx";
+    const char* cCoords1 = "cCoords1";
+    const char* cCoords2 = "cCoords2";
+
+    // Note: if implemented using texture3D in OpenGL ES older than OpenGL ES 3.0,
+    //       the shader might need "#extension GL_OES_texture_3D : enable".
+
+    GrGLFragmentShaderBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+
+    // Unpremultiply color
+    fsBuilder->codeAppendf("\tfloat %s = max(%s.a, 0.00001);\n", nonZeroAlpha, inputColor);
+    fsBuilder->codeAppendf("\tvec4 %s = vec4(%s.rgb / %s, %s);\n",
+                           unPMColor, inputColor, nonZeroAlpha, nonZeroAlpha);
+
+    // Fit input color into the cube.
+    fsBuilder->codeAppendf(
+        "vec3 %s = vec3(%s.rg * vec2((%s - 1.0) * %s) + vec2(0.5 * %s), %s.b * (%s - 1.0));\n",
+        cubeIdx, unPMColor, colorCubeSizeUni, colorCubeInvSizeUni, colorCubeInvSizeUni,
+        unPMColor, colorCubeSizeUni);
+
+    // Compute y coord for for texture fetches.
+    fsBuilder->codeAppendf("vec2 %s = vec2(%s.r, (floor(%s.b) + %s.g) * %s);\n",
+                           cCoords1, cubeIdx, cubeIdx, cubeIdx, colorCubeInvSizeUni);
+    fsBuilder->codeAppendf("vec2 %s = vec2(%s.r, (ceil(%s.b) + %s.g) * %s);\n",
+                           cCoords2, cubeIdx, cubeIdx, cubeIdx, colorCubeInvSizeUni);
+
+    // Apply the cube.
+    fsBuilder->codeAppendf("%s = vec4(mix(", outputColor);
+    fsBuilder->appendTextureLookup(samplers[0], cCoords1, coords[0].getType());
+    fsBuilder->codeAppend(".rgb, ");
+    fsBuilder->appendTextureLookup(samplers[0], cCoords2, coords[0].getType());
+
+    // Premultiply color by alpha. Note that the input alpha is not modified by this shader.
+    fsBuilder->codeAppendf(".rgb, fract(%s.b)) * vec3(%s), %s.a);\n",
+                           cubeIdx, nonZeroAlpha, inputColor);
+}
+
+void GrColorProfileEffect::GLProcessor::setData(const GrGLProgramDataManager& pdman,
+                                                const GrProcessor& proc) {
+    const GrColorProfileEffect& colorProfile = proc.cast<GrColorProfileEffect>();
+    SkScalar size = SkIntToScalar(colorProfile.colorCubeSize());
+    pdman.set1f(fColorCubeSizeUni, SkScalarToFloat(size));
+    pdman.set1f(fColorCubeInvSizeUni, SkScalarToFloat(SkScalarInvert(size)));
+}
+
+void GrColorProfileEffect::GLProcessor::GenKey(const GrProcessor& proc,
+                                               const GrGLCaps&, GrProcessorKeyBuilder* b) {
+    b->add32(1); // Always same shader for now
+}
+
+GrFragmentProcessor* SkColorCubeFilter::asFragmentProcessor(GrContext* context) const {
+    static const GrCacheID::Domain gCubeDomain = GrCacheID::GenerateDomain();
+
+    GrCacheID::Key key;
+    key.fData32[0] = fUniqueID;
+    key.fData32[1] = fCache.cubeDimension();
+    key.fData64[1] = 0;
+    GrCacheID cacheID(gCubeDomain, key);
+
+    GrTextureDesc desc;
+    desc.fWidth = fCache.cubeDimension();
+    desc.fHeight = fCache.cubeDimension() * fCache.cubeDimension();
+    desc.fConfig = kRGBA_8888_GrPixelConfig;
+
+    SkAutoTUnref<GrTexture> textureCube(
+        static_cast<GrTexture*>(context->findAndRefCachedResource(
+            GrTexturePriv::ComputeKey(context->getGpu(), NULL, desc, cacheID))));
+
+    if (!textureCube) {
+        textureCube.reset(context->createTexture(NULL, desc, cacheID, fCubeData->data(), 0));
+    }
+
+    return textureCube ? GrColorProfileEffect::Create(textureCube) : NULL;
+}
+#endif
index 1893ed0..9f5614c 100644 (file)
@@ -21,6 +21,7 @@
 #include "SkBlurDrawLooper.h"
 #include "SkBlurImageFilter.h"
 #include "SkBlurMaskFilter.h"
+#include "SkColorCubeFilter.h"
 #include "SkColorFilter.h"
 #include "SkColorFilterImageFilter.h"
 #include "SkColorMatrixFilter.h"
@@ -67,6 +68,7 @@ public:
         SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBitmapSource)
         SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBlurDrawLooper)
         SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBlurImageFilter)
+        SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkColorCubeFilter)
         SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkColorMatrixFilter)
         SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkColorShader)
         SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkComposePathEffect)
index 1893ed0..9f5614c 100644 (file)
@@ -21,6 +21,7 @@
 #include "SkBlurDrawLooper.h"
 #include "SkBlurImageFilter.h"
 #include "SkBlurMaskFilter.h"
+#include "SkColorCubeFilter.h"
 #include "SkColorFilter.h"
 #include "SkColorFilterImageFilter.h"
 #include "SkColorMatrixFilter.h"
@@ -67,6 +68,7 @@ public:
         SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBitmapSource)
         SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBlurDrawLooper)
         SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBlurImageFilter)
+        SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkColorCubeFilter)
         SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkColorMatrixFilter)
         SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkColorShader)
         SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkComposePathEffect)