From ce686270f533b9d741ef85661ef9d78517a01b86 Mon Sep 17 00:00:00 2001 From: sugoi Date: Thu, 9 Oct 2014 05:27:23 -0700 Subject: Adding 3D lut color filter Included in this cl is support for 3D textures. BUG=skia: Review URL: https://codereview.chromium.org/580863004 --- src/effects/SkColorCubeFilter.cpp | 376 ++++++++++++++++++++++++++ src/ports/SkGlobalInitialization_chromium.cpp | 2 + src/ports/SkGlobalInitialization_default.cpp | 2 + 3 files changed, 380 insertions(+) create mode 100644 src/effects/SkColorCubeFilter.cpp (limited to 'src') 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(); + return fColorCubeAccess.getTexture() == s.fColorCubeAccess.getTexture(); +} + +const GrBackendFragmentProcessorFactory& GrColorProfileEffect::getFactory() const { + return GrTBackendFragmentProcessorFactory::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(); + 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 textureCube( + static_cast(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) -- cgit v1.2.3