aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/effects
diff options
context:
space:
mode:
authorGravatar sugoi <sugoi@chromium.org>2014-10-09 05:27:23 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2014-10-09 05:27:23 -0700
commitce686270f533b9d741ef85661ef9d78517a01b86 (patch)
treec89179e16db02dfea5042aa51fff79fc129e643d /src/effects
parent9e61bb7815b133bc40ea7b00fccc853f4b728e3c (diff)
Adding 3D lut color filter
Included in this cl is support for 3D textures. BUG=skia: Review URL: https://codereview.chromium.org/580863004
Diffstat (limited to 'src/effects')
-rw-r--r--src/effects/SkColorCubeFilter.cpp376
1 files changed, 376 insertions, 0 deletions
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