1/* 2 * Copyright 2014 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#include "SkColorCubeFilter.h" 9#include "SkColorPriv.h" 10#include "SkOnce.h" 11#include "SkReadBuffer.h" 12#include "SkUnPreMultiply.h" 13#include "SkWriteBuffer.h" 14#if SK_SUPPORT_GPU 15#include "GrContext.h" 16#include "GrCoordTransform.h" 17#include "GrInvariantOutput.h" 18#include "GrTexturePriv.h" 19#include "SkGr.h" 20#include "gl/GrGLProcessor.h" 21#include "gl/builders/GrGLProgramBuilder.h" 22#endif 23 24/////////////////////////////////////////////////////////////////////////////// 25namespace { 26 27int32_t SkNextColorCubeUniqueID() { 28 static int32_t gColorCubeUniqueID; 29 // do a loop in case our global wraps around, as we never want to return a 0 30 int32_t genID; 31 do { 32 genID = sk_atomic_inc(&gColorCubeUniqueID) + 1; 33 } while (0 == genID); 34 return genID; 35} 36 37} // end namespace 38 39static const int MIN_CUBE_SIZE = 4; 40static const int MAX_CUBE_SIZE = 64; 41 42static bool is_valid_3D_lut(SkData* cubeData, int cubeDimension) { 43 size_t minMemorySize = sizeof(uint8_t) * 4 * cubeDimension * cubeDimension * cubeDimension; 44 return (cubeDimension >= MIN_CUBE_SIZE) && (cubeDimension <= MAX_CUBE_SIZE) && 45 (NULL != cubeData) && (cubeData->size() >= minMemorySize); 46} 47 48SkColorFilter* SkColorCubeFilter::Create(SkData* cubeData, int cubeDimension) { 49 if (!is_valid_3D_lut(cubeData, cubeDimension)) { 50 return NULL; 51 } 52 53 return SkNEW_ARGS(SkColorCubeFilter, (cubeData, cubeDimension)); 54} 55 56SkColorCubeFilter::SkColorCubeFilter(SkData* cubeData, int cubeDimension) 57 : fCubeData(SkRef(cubeData)) 58 , fUniqueID(SkNextColorCubeUniqueID()) 59 , fCache(cubeDimension) { 60} 61 62uint32_t SkColorCubeFilter::getFlags() const { 63 return this->INHERITED::getFlags() | kAlphaUnchanged_Flag; 64} 65 66SkColorCubeFilter::ColorCubeProcesingCache::ColorCubeProcesingCache(int cubeDimension) 67 : fCubeDimension(cubeDimension) 68 , fLutsInited(false) { 69 fColorToIndex[0] = fColorToIndex[1] = NULL; 70 fColorToFactors[0] = fColorToFactors[1] = NULL; 71 fColorToScalar = NULL; 72} 73 74void SkColorCubeFilter::ColorCubeProcesingCache::getProcessingLuts( 75 const int* (*colorToIndex)[2], const SkScalar* (*colorToFactors)[2], 76 const SkScalar** colorToScalar) { 77 SkOnce(&fLutsInited, &fLutsMutex, 78 SkColorCubeFilter::ColorCubeProcesingCache::initProcessingLuts, this); 79 SkASSERT((fColorToIndex[0] != NULL) && 80 (fColorToIndex[1] != NULL) && 81 (fColorToFactors[0] != NULL) && 82 (fColorToFactors[1] != NULL) && 83 (fColorToScalar != NULL)); 84 (*colorToIndex)[0] = fColorToIndex[0]; 85 (*colorToIndex)[1] = fColorToIndex[1]; 86 (*colorToFactors)[0] = fColorToFactors[0]; 87 (*colorToFactors)[1] = fColorToFactors[1]; 88 (*colorToScalar) = fColorToScalar; 89} 90 91void SkColorCubeFilter::ColorCubeProcesingCache::initProcessingLuts( 92 SkColorCubeFilter::ColorCubeProcesingCache* cache) { 93 static const SkScalar inv8bit = SkScalarInvert(SkIntToScalar(255)); 94 95 // We need 256 int * 2 for fColorToIndex, so a total of 512 int. 96 // We need 256 SkScalar * 2 for fColorToFactors and 256 SkScalar 97 // for fColorToScalar, so a total of 768 SkScalar. 98 cache->fLutStorage.reset(512 * sizeof(int) + 768 * sizeof(SkScalar)); 99 uint8_t* storage = (uint8_t*)cache->fLutStorage.get(); 100 cache->fColorToIndex[0] = (int*)storage; 101 cache->fColorToIndex[1] = cache->fColorToIndex[0] + 256; 102 cache->fColorToFactors[0] = (SkScalar*)(storage + (512 * sizeof(int))); 103 cache->fColorToFactors[1] = cache->fColorToFactors[0] + 256; 104 cache->fColorToScalar = cache->fColorToFactors[1] + 256; 105 106 SkScalar size = SkIntToScalar(cache->fCubeDimension); 107 SkScalar scale = (size - SK_Scalar1) * inv8bit; 108 109 for (int i = 0; i < 256; ++i) { 110 SkScalar index = scale * i; 111 cache->fColorToIndex[0][i] = SkScalarFloorToInt(index); 112 cache->fColorToIndex[1][i] = cache->fColorToIndex[0][i] + 1; 113 cache->fColorToScalar[i] = inv8bit * i; 114 if (cache->fColorToIndex[1][i] < cache->fCubeDimension) { 115 cache->fColorToFactors[1][i] = index - SkIntToScalar(cache->fColorToIndex[0][i]); 116 cache->fColorToFactors[0][i] = SK_Scalar1 - cache->fColorToFactors[1][i]; 117 } else { 118 cache->fColorToIndex[1][i] = cache->fColorToIndex[0][i]; 119 cache->fColorToFactors[0][i] = SK_Scalar1; 120 cache->fColorToFactors[1][i] = 0; 121 } 122 } 123} 124 125void SkColorCubeFilter::filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const { 126 const int* colorToIndex[2]; 127 const SkScalar* colorToFactors[2]; 128 const SkScalar* colorToScalar; 129 fCache.getProcessingLuts(&colorToIndex, &colorToFactors, &colorToScalar); 130 131 const int dim = fCache.cubeDimension(); 132 SkColor* colorCube = (SkColor*)fCubeData->data(); 133 for (int i = 0; i < count; ++i) { 134 SkColor inputColor = SkUnPreMultiply::PMColorToColor(src[i]); 135 uint8_t r = SkColorGetR(inputColor); 136 uint8_t g = SkColorGetG(inputColor); 137 uint8_t b = SkColorGetB(inputColor); 138 uint8_t a = SkColorGetA(inputColor); 139 SkScalar rOut(0), gOut(0), bOut(0); 140 for (int x = 0; x < 2; ++x) { 141 for (int y = 0; y < 2; ++y) { 142 for (int z = 0; z < 2; ++z) { 143 SkColor lutColor = colorCube[colorToIndex[x][r] + 144 (colorToIndex[y][g] + 145 colorToIndex[z][b] * dim) * dim]; 146 SkScalar factor = colorToFactors[x][r] * 147 colorToFactors[y][g] * 148 colorToFactors[z][b]; 149 rOut += colorToScalar[SkColorGetR(lutColor)] * factor; 150 gOut += colorToScalar[SkColorGetG(lutColor)] * factor; 151 bOut += colorToScalar[SkColorGetB(lutColor)] * factor; 152 } 153 } 154 } 155 const SkScalar aOut = SkIntToScalar(a); 156 dst[i] = SkPackARGB32(a, 157 SkScalarRoundToInt(rOut * aOut), 158 SkScalarRoundToInt(gOut * aOut), 159 SkScalarRoundToInt(bOut * aOut)); 160 } 161} 162 163SkFlattenable* SkColorCubeFilter::CreateProc(SkReadBuffer& buffer) { 164 int cubeDimension = buffer.readInt(); 165 SkAutoDataUnref cubeData(buffer.readByteArrayAsData()); 166 if (!buffer.validate(is_valid_3D_lut(cubeData, cubeDimension))) { 167 return NULL; 168 } 169 return Create(cubeData, cubeDimension); 170} 171 172void SkColorCubeFilter::flatten(SkWriteBuffer& buffer) const { 173 this->INHERITED::flatten(buffer); 174 buffer.writeInt(fCache.cubeDimension()); 175 buffer.writeDataAsByteArray(fCubeData); 176} 177 178#ifndef SK_IGNORE_TO_STRING 179void SkColorCubeFilter::toString(SkString* str) const { 180 str->append("SkColorCubeFilter "); 181} 182#endif 183 184/////////////////////////////////////////////////////////////////////////////// 185#if SK_SUPPORT_GPU 186 187class GrColorCubeEffect : public GrFragmentProcessor { 188public: 189 static GrFragmentProcessor* Create(GrTexture* colorCube) { 190 return (NULL != colorCube) ? SkNEW_ARGS(GrColorCubeEffect, (colorCube)) : NULL; 191 } 192 193 virtual ~GrColorCubeEffect(); 194 195 const char* name() const override { return "ColorCube"; } 196 197 virtual void getGLProcessorKey(const GrGLSLCaps& caps, 198 GrProcessorKeyBuilder* b) const override; 199 200 GrGLFragmentProcessor* createGLInstance() const override; 201 int colorCubeSize() const { return fColorCubeAccess.getTexture()->width(); } 202 203 204 void onComputeInvariantOutput(GrInvariantOutput*) const override; 205 206 class GLProcessor : public GrGLFragmentProcessor { 207 public: 208 GLProcessor(const GrProcessor&); 209 virtual ~GLProcessor(); 210 211 virtual void emitCode(GrGLFPBuilder*, 212 const GrFragmentProcessor&, 213 const char* outputColor, 214 const char* inputColor, 215 const TransformedCoordsArray&, 216 const TextureSamplerArray&) override; 217 218 static inline void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder*); 219 220 void setData(const GrGLProgramDataManager&, const GrProcessor&) override; 221 222 private: 223 GrGLProgramDataManager::UniformHandle fColorCubeSizeUni; 224 GrGLProgramDataManager::UniformHandle fColorCubeInvSizeUni; 225 226 typedef GrGLFragmentProcessor INHERITED; 227 }; 228 229private: 230 bool onIsEqual(const GrFragmentProcessor&) const override { return true; } 231 232 GrColorCubeEffect(GrTexture* colorCube); 233 234 GrTextureAccess fColorCubeAccess; 235 236 typedef GrFragmentProcessor INHERITED; 237}; 238 239/////////////////////////////////////////////////////////////////////////////// 240 241GrColorCubeEffect::GrColorCubeEffect(GrTexture* colorCube) 242 : fColorCubeAccess(colorCube, "bgra", GrTextureParams::kBilerp_FilterMode) { 243 this->initClassID<GrColorCubeEffect>(); 244 this->addTextureAccess(&fColorCubeAccess); 245} 246 247GrColorCubeEffect::~GrColorCubeEffect() { 248} 249 250void GrColorCubeEffect::getGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const { 251 GLProcessor::GenKey(*this, caps, b); 252} 253 254GrGLFragmentProcessor* GrColorCubeEffect::createGLInstance() const { 255 return SkNEW_ARGS(GLProcessor, (*this)); 256} 257 258void GrColorCubeEffect::onComputeInvariantOutput(GrInvariantOutput* inout) const { 259 inout->setToUnknown(GrInvariantOutput::kWill_ReadInput); 260} 261 262/////////////////////////////////////////////////////////////////////////////// 263 264GrColorCubeEffect::GLProcessor::GLProcessor(const GrProcessor&) { 265} 266 267GrColorCubeEffect::GLProcessor::~GLProcessor() { 268} 269 270void GrColorCubeEffect::GLProcessor::emitCode(GrGLFPBuilder* builder, 271 const GrFragmentProcessor&, 272 const char* outputColor, 273 const char* inputColor, 274 const TransformedCoordsArray& coords, 275 const TextureSamplerArray& samplers) { 276 if (NULL == inputColor) { 277 inputColor = "vec4(1)"; 278 } 279 280 fColorCubeSizeUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility, 281 kFloat_GrSLType, kDefault_GrSLPrecision, 282 "Size"); 283 const char* colorCubeSizeUni = builder->getUniformCStr(fColorCubeSizeUni); 284 fColorCubeInvSizeUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility, 285 kFloat_GrSLType, kDefault_GrSLPrecision, 286 "InvSize"); 287 const char* colorCubeInvSizeUni = builder->getUniformCStr(fColorCubeInvSizeUni); 288 289 const char* nonZeroAlpha = "nonZeroAlpha"; 290 const char* unPMColor = "unPMColor"; 291 const char* cubeIdx = "cubeIdx"; 292 const char* cCoords1 = "cCoords1"; 293 const char* cCoords2 = "cCoords2"; 294 295 // Note: if implemented using texture3D in OpenGL ES older than OpenGL ES 3.0, 296 // the shader might need "#extension GL_OES_texture_3D : enable". 297 298 GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder(); 299 300 // Unpremultiply color 301 fsBuilder->codeAppendf("\tfloat %s = max(%s.a, 0.00001);\n", nonZeroAlpha, inputColor); 302 fsBuilder->codeAppendf("\tvec4 %s = vec4(%s.rgb / %s, %s);\n", 303 unPMColor, inputColor, nonZeroAlpha, nonZeroAlpha); 304 305 // Fit input color into the cube. 306 fsBuilder->codeAppendf( 307 "vec3 %s = vec3(%s.rg * vec2((%s - 1.0) * %s) + vec2(0.5 * %s), %s.b * (%s - 1.0));\n", 308 cubeIdx, unPMColor, colorCubeSizeUni, colorCubeInvSizeUni, colorCubeInvSizeUni, 309 unPMColor, colorCubeSizeUni); 310 311 // Compute y coord for for texture fetches. 312 fsBuilder->codeAppendf("vec2 %s = vec2(%s.r, (floor(%s.b) + %s.g) * %s);\n", 313 cCoords1, cubeIdx, cubeIdx, cubeIdx, colorCubeInvSizeUni); 314 fsBuilder->codeAppendf("vec2 %s = vec2(%s.r, (ceil(%s.b) + %s.g) * %s);\n", 315 cCoords2, cubeIdx, cubeIdx, cubeIdx, colorCubeInvSizeUni); 316 317 // Apply the cube. 318 fsBuilder->codeAppendf("%s = vec4(mix(", outputColor); 319 fsBuilder->appendTextureLookup(samplers[0], cCoords1); 320 fsBuilder->codeAppend(".rgb, "); 321 fsBuilder->appendTextureLookup(samplers[0], cCoords2); 322 323 // Premultiply color by alpha. Note that the input alpha is not modified by this shader. 324 fsBuilder->codeAppendf(".rgb, fract(%s.b)) * vec3(%s), %s.a);\n", 325 cubeIdx, nonZeroAlpha, inputColor); 326} 327 328void GrColorCubeEffect::GLProcessor::setData(const GrGLProgramDataManager& pdman, 329 const GrProcessor& proc) { 330 const GrColorCubeEffect& colorCube = proc.cast<GrColorCubeEffect>(); 331 SkScalar size = SkIntToScalar(colorCube.colorCubeSize()); 332 pdman.set1f(fColorCubeSizeUni, SkScalarToFloat(size)); 333 pdman.set1f(fColorCubeInvSizeUni, SkScalarToFloat(SkScalarInvert(size))); 334} 335 336void GrColorCubeEffect::GLProcessor::GenKey(const GrProcessor& proc, 337 const GrGLSLCaps&, GrProcessorKeyBuilder* b) { 338} 339 340bool SkColorCubeFilter::asFragmentProcessors(GrContext* context, 341 SkTDArray<GrFragmentProcessor*>* array) const { 342 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); 343 GrUniqueKey key; 344 GrUniqueKey::Builder builder(&key, kDomain, 2); 345 builder[0] = fUniqueID; 346 builder[1] = fCache.cubeDimension(); 347 builder.finish(); 348 349 GrSurfaceDesc desc; 350 desc.fWidth = fCache.cubeDimension(); 351 desc.fHeight = fCache.cubeDimension() * fCache.cubeDimension(); 352 desc.fConfig = kRGBA_8888_GrPixelConfig; 353 354 SkAutoTUnref<GrTexture> textureCube( 355 context->textureProvider()->findAndRefTextureByUniqueKey(key)); 356 if (!textureCube) { 357 textureCube.reset(context->textureProvider()->createTexture( 358 desc, true, fCubeData->data(), 0)); 359 if (textureCube) { 360 context->textureProvider()->assignUniqueKeyToTexture(key, textureCube); 361 } 362 } 363 364 GrFragmentProcessor* frag = textureCube ? GrColorCubeEffect::Create(textureCube) : NULL; 365 if (frag) { 366 if (array) { 367 *array->append() = frag; 368 } 369 return true; 370 } 371 return false; 372} 373#endif 374