OLD | NEW |
(Empty) | |
| 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 "gl/GrGLProcessor.h" |
| 18 #include "gl/builders/GrGLProgramBuilder.h" |
| 19 #include "GrTBackendProcessorFactory.h" |
| 20 #include "GrTexturePriv.h" |
| 21 #include "SkGr.h" |
| 22 #endif |
| 23 |
| 24 /////////////////////////////////////////////////////////////////////////////// |
| 25 namespace { |
| 26 |
| 27 int32_t SkNextColorProfileUniqueID() { |
| 28 static int32_t gColorProfileUniqueID; |
| 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(&gColorProfileUniqueID) + 1; |
| 33 } while (0 == genID); |
| 34 return genID; |
| 35 } |
| 36 |
| 37 } // end namespace |
| 38 |
| 39 static const int MIN_CUBE_SIZE = 4; |
| 40 static const int MAX_CUBE_SIZE = 64; |
| 41 |
| 42 static 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 |
| 48 SkColorFilter* 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 |
| 56 SkColorCubeFilter::SkColorCubeFilter(SkData* cubeData, int cubeDimension) |
| 57 : fCubeData(SkRef(cubeData)) |
| 58 , fUniqueID(SkNextColorProfileUniqueID()) |
| 59 , fCache(cubeDimension) { |
| 60 } |
| 61 |
| 62 uint32_t SkColorCubeFilter::getFlags() const { |
| 63 return this->INHERITED::getFlags() | kAlphaUnchanged_Flag; |
| 64 } |
| 65 |
| 66 SkColorCubeFilter::ColorCubeProcesingCache::ColorCubeProcesingCache(int cubeDime
nsion) |
| 67 : fCubeDimension(cubeDimension) |
| 68 , fLutsInited(false) { |
| 69 fColorToIndex[0] = fColorToIndex[1] = NULL; |
| 70 fColorToFactors[0] = fColorToFactors[1] = NULL; |
| 71 fColorToScalar = NULL; |
| 72 } |
| 73 |
| 74 void 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 |
| 91 void 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->fColorTo
Index[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 |
| 125 void SkColorCubeFilter::filterSpan(const SkPMColor src[], int count, SkPMColor d
st[]) 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 |
| 163 #ifdef SK_SUPPORT_LEGACY_DEEPFLATTENING |
| 164 SkColorCubeFilter::SkColorCubeFilter(SkReadBuffer& buffer) |
| 165 : fCache(buffer.readInt()) { |
| 166 fCubeData.reset(buffer.readByteArrayAsData()); |
| 167 buffer.validate(is_valid_3D_lut(fCubeData, fCache.cubeDimension())); |
| 168 fUniqueID = SkNextColorProfileUniqueID(); |
| 169 } |
| 170 #endif |
| 171 |
| 172 SkFlattenable* SkColorCubeFilter::CreateProc(SkReadBuffer& buffer) { |
| 173 int cubeDimension = buffer.readInt(); |
| 174 SkData* cubeData = buffer.readByteArrayAsData(); |
| 175 if (!buffer.validate(is_valid_3D_lut(cubeData, cubeDimension))) { |
| 176 SkSafeUnref(cubeData); |
| 177 return NULL; |
| 178 } |
| 179 return Create(cubeData, cubeDimension); |
| 180 } |
| 181 |
| 182 void SkColorCubeFilter::flatten(SkWriteBuffer& buffer) const { |
| 183 this->INHERITED::flatten(buffer); |
| 184 buffer.writeInt(fCache.cubeDimension()); |
| 185 buffer.writeDataAsByteArray(fCubeData); |
| 186 } |
| 187 |
| 188 #ifndef SK_IGNORE_TO_STRING |
| 189 void SkColorCubeFilter::toString(SkString* str) const { |
| 190 str->append("SkColorCubeFilter "); |
| 191 } |
| 192 #endif |
| 193 |
| 194 /////////////////////////////////////////////////////////////////////////////// |
| 195 #if SK_SUPPORT_GPU |
| 196 class GrColorProfileEffect : public GrFragmentProcessor { |
| 197 public: |
| 198 static GrFragmentProcessor* Create(GrTexture* colorCube) { |
| 199 return (NULL != colorCube) ? SkNEW_ARGS(GrColorProfileEffect, (colorCube
)) : NULL; |
| 200 } |
| 201 |
| 202 virtual ~GrColorProfileEffect(); |
| 203 |
| 204 virtual const GrBackendFragmentProcessorFactory& getFactory() const SK_OVERR
IDE; |
| 205 int colorCubeSize() const { return fColorCubeAccess.getTexture()->width(); } |
| 206 |
| 207 static const char* Name() { return "ColorProfile"; } |
| 208 |
| 209 virtual void onComputeInvariantOutput(GrProcessor::InvariantOutput*) const S
K_OVERRIDE; |
| 210 |
| 211 class GLProcessor : public GrGLFragmentProcessor { |
| 212 public: |
| 213 GLProcessor(const GrBackendProcessorFactory& factory, const GrProcessor&
); |
| 214 virtual ~GLProcessor(); |
| 215 |
| 216 virtual void emitCode(GrGLProgramBuilder*, |
| 217 const GrFragmentProcessor&, |
| 218 const GrProcessorKey&, |
| 219 const char* outputColor, |
| 220 const char* inputColor, |
| 221 const TransformedCoordsArray&, |
| 222 const TextureSamplerArray&) SK_OVERRIDE; |
| 223 |
| 224 static inline void GenKey(const GrProcessor&, const GrGLCaps&, GrProcess
orKeyBuilder*); |
| 225 |
| 226 virtual void setData(const GrGLProgramDataManager&, const GrProcessor&)
SK_OVERRIDE; |
| 227 |
| 228 private: |
| 229 GrGLProgramDataManager::UniformHandle fColorCubeSizeUni; |
| 230 GrGLProgramDataManager::UniformHandle fColorCubeInvSizeUni; |
| 231 |
| 232 typedef GrGLFragmentProcessor INHERITED; |
| 233 }; |
| 234 |
| 235 private: |
| 236 virtual bool onIsEqual(const GrProcessor&) const SK_OVERRIDE; |
| 237 |
| 238 GrColorProfileEffect(GrTexture* colorCube); |
| 239 |
| 240 GrCoordTransform fColorCubeTransform; |
| 241 GrTextureAccess fColorCubeAccess; |
| 242 |
| 243 typedef GrFragmentProcessor INHERITED; |
| 244 }; |
| 245 |
| 246 /////////////////////////////////////////////////////////////////////////////// |
| 247 |
| 248 GrColorProfileEffect::GrColorProfileEffect(GrTexture* colorCube) |
| 249 : fColorCubeTransform(kLocal_GrCoordSet, colorCube) |
| 250 , fColorCubeAccess(colorCube, "bgra", GrTextureParams::kBilerp_FilterMode) { |
| 251 this->addCoordTransform(&fColorCubeTransform); |
| 252 this->addTextureAccess(&fColorCubeAccess); |
| 253 } |
| 254 |
| 255 GrColorProfileEffect::~GrColorProfileEffect() { |
| 256 } |
| 257 |
| 258 bool GrColorProfileEffect::onIsEqual(const GrProcessor& sBase) const { |
| 259 const GrColorProfileEffect& s = sBase.cast<GrColorProfileEffect>(); |
| 260 return fColorCubeAccess.getTexture() == s.fColorCubeAccess.getTexture(); |
| 261 } |
| 262 |
| 263 const GrBackendFragmentProcessorFactory& GrColorProfileEffect::getFactory() cons
t { |
| 264 return GrTBackendFragmentProcessorFactory<GrColorProfileEffect>::getInstance
(); |
| 265 } |
| 266 |
| 267 void GrColorProfileEffect::onComputeInvariantOutput(InvariantOutput* inout) cons
t { |
| 268 inout->fValidFlags = 0; |
| 269 inout->fIsSingleComponent = false; |
| 270 } |
| 271 |
| 272 /////////////////////////////////////////////////////////////////////////////// |
| 273 |
| 274 GrColorProfileEffect::GLProcessor::GLProcessor(const GrBackendProcessorFactory&
factory, |
| 275 const GrProcessor&) |
| 276 : INHERITED(factory) { |
| 277 } |
| 278 |
| 279 GrColorProfileEffect::GLProcessor::~GLProcessor() { |
| 280 } |
| 281 |
| 282 void GrColorProfileEffect::GLProcessor::emitCode(GrGLProgramBuilder* builder, |
| 283 const GrFragmentProcessor&, |
| 284 const GrProcessorKey&, |
| 285 const char* outputColor, |
| 286 const char* inputColor, |
| 287 const TransformedCoordsArray& c
oords, |
| 288 const TextureSamplerArray& samp
lers) { |
| 289 if (NULL == inputColor) { |
| 290 inputColor = "vec4(1)"; |
| 291 } |
| 292 |
| 293 fColorCubeSizeUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibi
lity, |
| 294 kFloat_GrSLType, "Size"); |
| 295 const char* colorCubeSizeUni = builder->getUniformCStr(fColorCubeSizeUni); |
| 296 fColorCubeInvSizeUni = builder->addUniform(GrGLProgramBuilder::kFragment_Vis
ibility, |
| 297 kFloat_GrSLType, "InvSize"); |
| 298 const char* colorCubeInvSizeUni = builder->getUniformCStr(fColorCubeInvSizeU
ni); |
| 299 |
| 300 const char* nonZeroAlpha = "nonZeroAlpha"; |
| 301 const char* unPMColor = "unPMColor"; |
| 302 const char* cubeIdx = "cubeIdx"; |
| 303 const char* cCoords1 = "cCoords1"; |
| 304 const char* cCoords2 = "cCoords2"; |
| 305 |
| 306 // Note: if implemented using texture3D in OpenGL ES older than OpenGL ES 3.
0, |
| 307 // the shader might need "#extension GL_OES_texture_3D : enable". |
| 308 |
| 309 GrGLFragmentShaderBuilder* fsBuilder = builder->getFragmentShaderBuilder(); |
| 310 |
| 311 // Unpremultiply color |
| 312 fsBuilder->codeAppendf("\tfloat %s = max(%s.a, 0.00001);\n", nonZeroAlpha, i
nputColor); |
| 313 fsBuilder->codeAppendf("\tvec4 %s = vec4(%s.rgb / %s, %s);\n", |
| 314 unPMColor, inputColor, nonZeroAlpha, nonZeroAlpha); |
| 315 |
| 316 // Fit input color into the cube. |
| 317 fsBuilder->codeAppendf( |
| 318 "vec3 %s = vec3(%s.rg * vec2((%s - 1.0) * %s) + vec2(0.5 * %s), %s.b * (
%s - 1.0));\n", |
| 319 cubeIdx, unPMColor, colorCubeSizeUni, colorCubeInvSizeUni, colorCubeInvS
izeUni, |
| 320 unPMColor, colorCubeSizeUni); |
| 321 |
| 322 // Compute y coord for for texture fetches. |
| 323 fsBuilder->codeAppendf("vec2 %s = vec2(%s.r, (floor(%s.b) + %s.g) * %s);\n", |
| 324 cCoords1, cubeIdx, cubeIdx, cubeIdx, colorCubeInvSize
Uni); |
| 325 fsBuilder->codeAppendf("vec2 %s = vec2(%s.r, (ceil(%s.b) + %s.g) * %s);\n", |
| 326 cCoords2, cubeIdx, cubeIdx, cubeIdx, colorCubeInvSize
Uni); |
| 327 |
| 328 // Apply the cube. |
| 329 fsBuilder->codeAppendf("%s = vec4(mix(", outputColor); |
| 330 fsBuilder->appendTextureLookup(samplers[0], cCoords1, coords[0].getType()); |
| 331 fsBuilder->codeAppend(".rgb, "); |
| 332 fsBuilder->appendTextureLookup(samplers[0], cCoords2, coords[0].getType()); |
| 333 |
| 334 // Premultiply color by alpha. Note that the input alpha is not modified by
this shader. |
| 335 fsBuilder->codeAppendf(".rgb, fract(%s.b)) * vec3(%s), %s.a);\n", |
| 336 cubeIdx, nonZeroAlpha, inputColor); |
| 337 } |
| 338 |
| 339 void GrColorProfileEffect::GLProcessor::setData(const GrGLProgramDataManager& pd
man, |
| 340 const GrProcessor& proc) { |
| 341 const GrColorProfileEffect& colorProfile = proc.cast<GrColorProfileEffect>()
; |
| 342 SkScalar size = SkIntToScalar(colorProfile.colorCubeSize()); |
| 343 pdman.set1f(fColorCubeSizeUni, SkScalarToFloat(size)); |
| 344 pdman.set1f(fColorCubeInvSizeUni, SkScalarToFloat(SkScalarInvert(size))); |
| 345 } |
| 346 |
| 347 void GrColorProfileEffect::GLProcessor::GenKey(const GrProcessor& proc, |
| 348 const GrGLCaps&, GrProcessorKeyBu
ilder* b) { |
| 349 b->add32(1); // Always same shader for now |
| 350 } |
| 351 |
| 352 GrFragmentProcessor* SkColorCubeFilter::asFragmentProcessor(GrContext* context)
const { |
| 353 static const GrCacheID::Domain gCubeDomain = GrCacheID::GenerateDomain(); |
| 354 |
| 355 GrCacheID::Key key; |
| 356 key.fData32[0] = fUniqueID; |
| 357 key.fData32[1] = fCache.cubeDimension(); |
| 358 key.fData64[1] = 0; |
| 359 GrCacheID cacheID(gCubeDomain, key); |
| 360 |
| 361 GrTextureDesc desc; |
| 362 desc.fWidth = fCache.cubeDimension(); |
| 363 desc.fHeight = fCache.cubeDimension() * fCache.cubeDimension(); |
| 364 desc.fConfig = kRGBA_8888_GrPixelConfig; |
| 365 |
| 366 SkAutoTUnref<GrTexture> textureCube( |
| 367 static_cast<GrTexture*>(context->findAndRefCachedResource( |
| 368 GrTexturePriv::ComputeKey(context->getGpu(), NULL, desc, cacheID))))
; |
| 369 |
| 370 if (!textureCube) { |
| 371 textureCube.reset(context->createTexture(NULL, desc, cacheID, fCubeData-
>data(), 0)); |
| 372 } |
| 373 |
| 374 return textureCube ? GrColorProfileEffect::Create(textureCube) : NULL; |
| 375 } |
| 376 #endif |
OLD | NEW |