Chromium Code Reviews| Index: src/effects/SkColorProfileFilter.cpp |
| diff --git a/src/effects/SkColorProfileFilter.cpp b/src/effects/SkColorProfileFilter.cpp |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..0302c280bd867302c37c56616a29995731158e34 |
| --- /dev/null |
| +++ b/src/effects/SkColorProfileFilter.cpp |
| @@ -0,0 +1,310 @@ |
| +/* |
| + * Copyright 2013 Google Inc. |
| + * |
| + * Use of this source code is governed by a BSD-style license that can be |
| + * found in the LICENSE file. |
| + */ |
| + |
| +#include "SkColorProfileFilter.h" |
| +#include "SkColorPriv.h" |
| +#include "SkReadBuffer.h" |
| +#include "SkUnPreMultiply.h" |
| +#include "SkWriteBuffer.h" |
| +#if SK_SUPPORT_GPU |
| +#include "GrContext.h" |
| +#include "GrCoordTransform.h" |
| +#include "gl/GrGLEffect.h" |
| +#include "gl/builders/GrGLProgramBuilder.h" |
| +#include "GrTBackendEffectFactory.h" |
| +#include "SkGr.h" |
| +#endif |
| + |
| +/////////////////////////////////////////////////////////////////////////////// |
| + |
| +static const int MIN_CUBE_SIZE = 4; |
| +static const int MAX_CUBE_SIZE = 64; |
| + |
| +static bool is_valid_3D_lut(const SkBitmap& bitmap) { |
| + int colorCubeSize = bitmap.width(); |
| + return bitmap.is3DLut() && |
| + (colorCubeSize >= MIN_CUBE_SIZE) && |
| + (colorCubeSize <= MAX_CUBE_SIZE); |
| +} |
| + |
| +SkColorProfileFilter* SkColorProfileFilter::Create(const SkBitmap& bitmap) { |
| + if (!is_valid_3D_lut(bitmap)) { |
| + return NULL; |
| + } |
| + |
| + return SkNEW_ARGS(SkColorProfileFilter, (bitmap)); |
| +} |
| + |
| +SkColorProfileFilter::SkColorProfileFilter(const SkBitmap& bitmap) |
| + : fColorCube(bitmap) |
| + , fLutStorage(0) |
| +{ |
| + fColorToIndex[0] = fColorToIndex[1] = NULL; |
| + fColorToFactors[0] = fColorToFactors[1] = NULL; |
| +} |
| + |
| +void SkColorProfileFilter::initProcessingLuts() { |
| + if ((fColorToIndex[0] != NULL) && |
| + (fColorToIndex[1] != NULL) && |
| + (fColorToFactors[0] != NULL) && |
| + (fColorToFactors[1] != NULL)) { |
| + return; |
| + } |
| + fLutStorage.reset(512 * (sizeof(int) + sizeof(SkScalar))); |
| + uint8_t* storage = (uint8_t*)fLutStorage.get(); |
| + fColorToIndex[0] = (int*)storage; |
| + fColorToIndex[1] = fColorToIndex[0] + 256; |
| + fColorToFactors[0] = (SkScalar*)(storage + (512 * sizeof(int))); |
| + fColorToFactors[1] = fColorToFactors[0] + 256; |
| + |
| + int colorCubeSize = fColorCube.width(); |
| + SkScalar size = SkIntToScalar(colorCubeSize); |
| + SkScalar scale = SkScalarDiv((size - SK_Scalar1), 255.0f); |
| + |
| + for (int i = 0; i < 256; ++i) { |
| + SkScalar index = SkScalarMul(i, scale); |
| + fColorToIndex[0][i] = SkScalarFloorToInt(index); |
| + fColorToIndex[1][i] = fColorToIndex[0][i] + 1; |
| + if (fColorToIndex[1][i] < colorCubeSize) { |
| + fColorToFactors[1][i] = index - SkIntToScalar(fColorToIndex[0][i]); |
| + fColorToFactors[0][i] = SK_Scalar1 - fColorToFactors[1][i]; |
| + } else { |
| + fColorToIndex[1][i] = fColorToIndex[0][i]; |
| + fColorToFactors[0][i] = SK_Scalar1; |
| + fColorToFactors[1][i] = 0; |
| + } |
| + } |
| +} |
| + |
| +void SkColorProfileFilter::filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const { |
| + const_cast<SkColorProfileFilter*>(this)->initProcessingLuts(); |
|
sugoi1
2014/09/24 19:38:42
Hi Mike and Brian, I have an issue here. initProce
|
| + |
| + SkAutoLockPixels alp(fColorCube); |
| + if (!fColorCube.getPixels()) { |
| + return; |
| + } |
| + |
| + int colorCubeSize = fColorCube.width(); |
| + 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) { |
| + int rIndex = fColorToIndex[x][r]; |
| + int gIndex = fColorToIndex[y][g]; |
| + int bIndex = fColorToIndex[z][b]; |
| + SkScalar factor = SkScalarMul(fColorToFactors[x][r], |
| + SkScalarMul(fColorToFactors[y][g], |
| + fColorToFactors[z][b])); |
| + SkPMColor lutColor = |
| + *fColorCube.getAddr32(rIndex, gIndex + colorCubeSize * bIndex); |
| + rOut += SkScalarMul(SkColorGetR(lutColor), factor); |
| + gOut += SkScalarMul(SkColorGetG(lutColor), factor); |
| + bOut += SkScalarMul(SkColorGetB(lutColor), factor); |
| + } |
| + } |
| + } |
| + dst[i] = SkPreMultiplyColor(SkColorSetARGB(a, |
| + SkScalarRoundToInt(rOut), |
| + SkScalarRoundToInt(gOut), |
| + SkScalarRoundToInt(bOut))); |
| + } |
| +} |
| + |
| +#ifdef SK_SUPPORT_LEGACY_DEEPFLATTENING |
| +SkColorProfileFilter::SkColorProfileFilter(SkReadBuffer& buffer) { |
| + if (buffer.readBitmap(&fColorCube)) { |
| + if (fColorCube.is3DLut()) { |
| + fColorCube.setIs3DLut(true); // This will validate that the bitmap is a valid 3D lut |
| + } |
| + buffer.validate(is_valid_3D_lut(fColorCube)); |
| + } |
| +} |
| +#endif |
| + |
| +SkFlattenable* SkColorProfileFilter::CreateProc(SkReadBuffer& buffer) { |
| + SkBitmap colorCube; |
| + return buffer.readBitmap(&colorCube) ? Create(colorCube) : NULL; |
| +} |
| + |
| +void SkColorProfileFilter::flatten(SkWriteBuffer& buffer) const { |
| + this->INHERITED::flatten(buffer); |
| + buffer.writeBitmap(fColorCube); |
| +} |
| + |
| +#ifndef SK_IGNORE_TO_STRING |
| +void SkColorProfileFilter::toString(SkString* str) const { |
| + str->append("SkColorProfileFilter "); |
| +} |
| +#endif |
| + |
| +/////////////////////////////////////////////////////////////////////////////// |
| + |
| +#if SK_SUPPORT_GPU |
| +class GrGLColorProfileEffect : public GrGLEffect { |
| +public: |
| + GrGLColorProfileEffect(const GrBackendEffectFactory& factory, |
| + const GrEffect& effect); |
| + virtual ~GrGLColorProfileEffect(); |
| + |
| + virtual void emitCode(GrGLProgramBuilder*, |
| + const GrEffect&, |
| + const GrEffectKey&, |
| + const char* outputColor, |
| + const char* inputColor, |
| + const TransformedCoordsArray&, |
| + const TextureSamplerArray&) SK_OVERRIDE; |
| + |
| + static inline void GenKey(const GrEffect&, const GrGLCaps&, GrEffectKeyBuilder*); |
| + |
| + virtual void setData(const GrGLProgramDataManager&, const GrEffect&) SK_OVERRIDE; |
| + |
| +private: |
| + GrGLProgramDataManager::UniformHandle fColorCubeOffsetUni; |
| + GrGLProgramDataManager::UniformHandle fColorCubeScaleUni; |
| + |
| + typedef GrGLEffect INHERITED; |
| +}; |
| + |
| +/////////////////////////////////////////////////////////////////////////////// |
| + |
| +class GrColorProfileEffect : public GrEffect { |
| +public: |
| + static GrEffect* Create(GrTexture* colorCube) { |
| + return (NULL != colorCube) ? SkNEW_ARGS(GrColorProfileEffect, (colorCube)) : NULL; |
| + } |
| + |
| + virtual ~GrColorProfileEffect(); |
| + |
| + virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE; |
| + int colorCubeSize() const { return fColorCubeAccess.getTexture()->width(); } |
| + |
| + typedef GrGLColorProfileEffect GLEffect; |
| + static const char* Name() { return "ColorProfile"; } |
| + |
| + virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE; |
| + |
| +private: |
| + virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE; |
| + |
| + GrColorProfileEffect(GrTexture* colorCube); |
| + |
| + GR_DECLARE_EFFECT_TEST; |
| + |
| + GrCoordTransform fColorCubeTransform; |
| + GrTextureAccess fColorCubeAccess; |
| + |
| + typedef GrEffect INHERITED; |
| +}; |
| + |
| +/////////////////////////////////////////////////////////////////////////////// |
| + |
| +GrColorProfileEffect::GrColorProfileEffect(GrTexture* colorCube) |
| + : fColorCubeTransform(kLocal_GrCoordSet, colorCube) |
| + , fColorCubeAccess(colorCube, GrTextureParams::kBilerp_FilterMode) { |
| + this->addCoordTransform(&fColorCubeTransform); |
| + this->addTextureAccess(&fColorCubeAccess); |
| +} |
| + |
| +GrColorProfileEffect::~GrColorProfileEffect() { |
| +} |
| + |
| +bool GrColorProfileEffect::onIsEqual(const GrEffect& sBase) const { |
| + const GrColorProfileEffect& s = sBase.cast<GrColorProfileEffect>(); |
| + return fColorCubeAccess.getTexture() == s.fColorCubeAccess.getTexture(); |
| +} |
| + |
| +const GrBackendEffectFactory& GrColorProfileEffect::getFactory() const { |
| + return GrTBackendEffectFactory<GrColorProfileEffect>::getInstance(); |
| +} |
| + |
| +void GrColorProfileEffect::getConstantColorComponents(GrColor*, |
| + uint32_t* validFlags) const { |
| + *validFlags = 0; |
| +} |
| + |
| +/////////////////////////////////////////////////////////////////////////////// |
| + |
| +GR_DEFINE_EFFECT_TEST(GrColorProfileEffect); |
| + |
| +GrEffect* GrColorProfileEffect::TestCreate(SkRandom* random, |
| + GrContext*, |
| + const GrDrawTargetCaps&, |
| + GrTexture* textures[]) { |
| + // FIXME: get a 3D texure in the test |
| + return GrColorProfileEffect::Create(NULL); |
| +} |
| + |
| +/////////////////////////////////////////////////////////////////////////////// |
| + |
| +GrGLColorProfileEffect::GrGLColorProfileEffect(const GrBackendEffectFactory& factory, |
| + const GrEffect& effect) |
| + : INHERITED(factory) { |
| +} |
| + |
| +GrGLColorProfileEffect::~GrGLColorProfileEffect() { |
| +} |
| + |
| +void GrGLColorProfileEffect::emitCode(GrGLProgramBuilder* builder, |
| + const GrEffect&, |
| + const GrEffectKey& key, |
| + const char* outputColor, |
| + const char* inputColor, |
| + const TransformedCoordsArray& coords, |
| + const TextureSamplerArray& samplers) { |
| + if (NULL == inputColor) { |
| + inputColor = "vec4(1)"; |
| + } |
| + |
| + fColorCubeOffsetUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility, |
| + kFloat_GrSLType, "Offset"); |
| + const char* colorCubeOffsetUni = builder->getUniformCStr(fColorCubeOffsetUni); |
| + fColorCubeScaleUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility, |
| + kFloat_GrSLType, "Scale"); |
| + const char* colorCubeScaleUni = builder->getUniformCStr(fColorCubeScaleUni); |
| + |
| + const char* cCoords = "cCoords"; |
| + |
| + GrGLFragmentShaderBuilder* fsBuilder = builder->getFragmentShaderBuilder(); |
| + |
| + // Fit input color into the cube |
| + fsBuilder->codeAppendf("vec3 %s = %s.rgb * vec3(%s) + vec3(%s);\n", |
| + cCoords, inputColor, colorCubeScaleUni, colorCubeOffsetUni); |
| + |
| + // Apply the cube |
| + fsBuilder->codeAppendf("%s = vec4(", outputColor); |
| + fsBuilder->appendTextureLookup(samplers[0], cCoords, coords[0].getType()); |
| + fsBuilder->codeAppendf(".rgb, %s.a);\n", inputColor); // Alpha is not modified by this |
| +} |
| + |
| +void GrGLColorProfileEffect::setData(const GrGLProgramDataManager& pdman, |
| + const GrEffect& effect) { |
| + const GrColorProfileEffect& colorProfile = effect.cast<GrColorProfileEffect>(); |
| + SkScalar size = SkIntToScalar(colorProfile.colorCubeSize()); |
| + SkScalar invSize = SkScalarInvert(size); |
| + pdman.set1f(fColorCubeOffsetUni, SkScalarToFloat(SkScalarHalf(invSize))); |
| + pdman.set1f(fColorCubeScaleUni, SkScalarToFloat(SkScalarMul((size - SK_Scalar1), invSize))); |
| +} |
| + |
| +void GrGLColorProfileEffect::GenKey(const GrEffect& effect, |
| + const GrGLCaps&, GrEffectKeyBuilder* b) { |
| + b->add32(1); // Always same shader for now |
| +} |
| + |
| +GrEffect* SkColorProfileFilter::asNewEffect(GrContext* context) const { |
| + GrTexture* colorCubeTex = fColorCube.getTexture(); |
| + if (NULL == colorCubeTex) { |
| + colorCubeTex = GrLockAndRefCachedBitmapTexture(context, fColorCube, NULL); |
| + } |
| + return (NULL == colorCubeTex) ? NULL : GrColorProfileEffect::Create(colorCubeTex); |
| +} |
| +#endif |