diff options
-rw-r--r-- | bench/ColorCubeBench.cpp | 112 | ||||
-rw-r--r-- | gm/colorcube.cpp | 141 | ||||
-rw-r--r-- | gyp/bench.gypi | 1 | ||||
-rw-r--r-- | gyp/effects.gypi | 2 | ||||
-rw-r--r-- | gyp/gmslides.gypi | 1 | ||||
-rw-r--r-- | include/effects/SkColorCubeFilter.h | 79 | ||||
-rw-r--r-- | src/effects/SkColorCubeFilter.cpp | 376 | ||||
-rw-r--r-- | src/ports/SkGlobalInitialization_chromium.cpp | 2 | ||||
-rw-r--r-- | src/ports/SkGlobalInitialization_default.cpp | 2 |
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) |