aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--bench/ColorCubeBench.cpp112
-rw-r--r--gm/colorcube.cpp141
-rw-r--r--gyp/bench.gypi1
-rw-r--r--gyp/effects.gypi2
-rw-r--r--gyp/gmslides.gypi1
-rw-r--r--include/effects/SkColorCubeFilter.h79
-rw-r--r--src/effects/SkColorCubeFilter.cpp376
-rw-r--r--src/ports/SkGlobalInitialization_chromium.cpp2
-rw-r--r--src/ports/SkGlobalInitialization_default.cpp2
9 files changed, 716 insertions, 0 deletions
diff --git a/bench/ColorCubeBench.cpp b/bench/ColorCubeBench.cpp
new file mode 100644
index 0000000000..16ed4ce892
--- /dev/null
+++ b/bench/ColorCubeBench.cpp
@@ -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
index 0000000000..59463fcd9e
--- /dev/null
+++ b/gm/colorcube.cpp
@@ -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);
+
+}
diff --git a/gyp/bench.gypi b/gyp/bench.gypi
index adb98a1307..c2c8ec90fb 100644
--- a/gyp/bench.gypi
+++ b/gyp/bench.gypi
@@ -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',
diff --git a/gyp/effects.gypi b/gyp/effects.gypi
index da2f5dbe96..c82d886318 100644
--- a/gyp/effects.gypi
+++ b/gyp/effects.gypi
@@ -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',
diff --git a/gyp/gmslides.gypi b/gyp/gmslides.gypi
index 557cc4aa4b..58bf17742d 100644
--- a/gyp/gmslides.gypi
+++ b/gyp/gmslides.gypi
@@ -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
index 0000000000..2cfee45e9c
--- /dev/null
+++ b/include/effects/SkColorCubeFilter.h
@@ -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
index 0000000000..cf3126bf39
--- /dev/null
+++ b/src/effects/SkColorCubeFilter.cpp
@@ -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
diff --git a/src/ports/SkGlobalInitialization_chromium.cpp b/src/ports/SkGlobalInitialization_chromium.cpp
index 1893ed01fc..9f5614cb74 100644
--- a/src/ports/SkGlobalInitialization_chromium.cpp
+++ b/src/ports/SkGlobalInitialization_chromium.cpp
@@ -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)
diff --git a/src/ports/SkGlobalInitialization_default.cpp b/src/ports/SkGlobalInitialization_default.cpp
index 1893ed01fc..9f5614cb74 100644
--- a/src/ports/SkGlobalInitialization_default.cpp
+++ b/src/ports/SkGlobalInitialization_default.cpp
@@ -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)