diff options
Diffstat (limited to 'src/effects')
-rw-r--r-- | src/effects/gradients/SkGradientShader.cpp | 134 | ||||
-rw-r--r-- | src/effects/gradients/SkGradientShaderPriv.h | 12 |
2 files changed, 131 insertions, 15 deletions
diff --git a/src/effects/gradients/SkGradientShader.cpp b/src/effects/gradients/SkGradientShader.cpp index 4f174d72b9..27f6136403 100644 --- a/src/effects/gradients/SkGradientShader.cpp +++ b/src/effects/gradients/SkGradientShader.cpp @@ -7,6 +7,7 @@ #include "Sk4fLinearGradient.h" #include "SkGradientShaderPriv.h" +#include "SkHalf.h" #include "SkLinearGradient.h" #include "SkRadialGradient.h" #include "SkTwoPointConicalGradient.h" @@ -544,6 +545,62 @@ void SkGradientShaderBase::GradientShaderCache::initCache32(GradientShaderCache* } } +void SkGradientShaderBase::initLinearBitmap(SkBitmap* bitmap) const { + const bool interpInPremul = SkToBool(fGradFlags & + SkGradientShader::kInterpolateColorsInPremul_Flag); + bitmap->lockPixels(); + SkHalf* pixelsF16 = reinterpret_cast<SkHalf*>(bitmap->getPixels()); + uint32_t* pixelsS32 = reinterpret_cast<uint32_t*>(bitmap->getPixels()); + + typedef std::function<void(const Sk4f&, int)> pixelWriteFn_t; + + pixelWriteFn_t writeF16Pixel = [&](const Sk4f& x, int index) { + Sk4h c = SkFloatToHalf_finite_ftz(x); + pixelsF16[4*index+0] = c[0]; + pixelsF16[4*index+1] = c[1]; + pixelsF16[4*index+2] = c[2]; + pixelsF16[4*index+3] = c[3]; + }; + pixelWriteFn_t writeS32Pixel = [&](const Sk4f& c, int index) { + pixelsS32[index] = Sk4f_toS32(c); + }; + + pixelWriteFn_t writeSizedPixel = + (kRGBA_F16_SkColorType == bitmap->colorType()) ? writeF16Pixel : writeS32Pixel; + pixelWriteFn_t writeUnpremulPixel = [&](const Sk4f& c, int index) { + writeSizedPixel(c * Sk4f(c[3], c[3], c[3], 1.0f), index); + }; + + pixelWriteFn_t writePixel = interpInPremul ? writeSizedPixel : writeUnpremulPixel; + + int prevIndex = 0; + for (int i = 1; i < fColorCount; i++) { + int nextIndex = (fColorCount == 2) ? (kCache32Count - 1) + : SkFixedToFFFF(fRecs[i].fPos) >> kCache32Shift; + SkASSERT(nextIndex < kCache32Count); + + if (nextIndex > prevIndex) { + Sk4f c0 = Sk4f::Load(fOrigColors4f[i - 1].vec()); + Sk4f c1 = Sk4f::Load(fOrigColors4f[i].vec()); + if (interpInPremul) { + c0 = c0 * Sk4f(c0[3], c0[3], c0[3], 1.0f); + c1 = c1 * Sk4f(c1[3], c1[3], c1[3], 1.0f); + } + + Sk4f step = Sk4f(1.0f / static_cast<float>(nextIndex - prevIndex)); + Sk4f delta = (c1 - c0) * step; + + for (int curIndex = prevIndex; curIndex <= nextIndex; ++curIndex) { + writePixel(c0, curIndex); + c0 += delta; + } + } + prevIndex = nextIndex; + } + SkASSERT(prevIndex == kCache32Count - 1); + bitmap->unlockPixels(); +} + /* * The gradient holds a cache for the most recent value of alpha. Successive * callers with the same alpha value will share the same cache. @@ -570,13 +627,13 @@ SK_DECLARE_STATIC_MUTEX(gGradientCacheMutex); * colors and positions. Note: we don't try to flatten the fMapper, so if one * is present, we skip the cache for now. */ -void SkGradientShaderBase::getGradientTableBitmap(SkBitmap* bitmap) const { - // our caller assumes no external alpha, so we ensure that our cache is - // built with 0xFF +void SkGradientShaderBase::getGradientTableBitmap(SkBitmap* bitmap, + GradientBitmapType bitmapType) const { + // our caller assumes no external alpha, so we ensure that our cache is built with 0xFF SkAutoTUnref<GradientShaderCache> cache(this->refCache(0xFF, true)); - // build our key: [numColors + colors[] + {positions[]} + flags ] - int count = 1 + fColorCount + 1; + // build our key: [numColors + colors[] + {positions[]} + flags + colorType ] + int count = 1 + fColorCount + 1 + 1; if (fColorCount > 2) { count += fColorCount - 1; // fRecs[].fPos } @@ -593,12 +650,13 @@ void SkGradientShaderBase::getGradientTableBitmap(SkBitmap* bitmap) const { } } *buffer++ = fGradFlags; + *buffer++ = static_cast<int32_t>(bitmapType); SkASSERT(buffer - storage.get() == count); /////////////////////////////////// static SkGradientBitmapCache* gCache; - // each cache cost 1K of RAM, since each bitmap will be 1x256 at 32bpp + // each cache cost 1K or 2K of RAM, since each bitmap will be 1x256 at either 32bpp or 64bpp static const int MAX_NUM_CACHED_GRADIENT_BITMAPS = 32; SkAutoMutexAcquire ama(gGradientCacheMutex); @@ -608,11 +666,35 @@ void SkGradientShaderBase::getGradientTableBitmap(SkBitmap* bitmap) const { size_t size = count * sizeof(int32_t); if (!gCache->find(storage.get(), size, bitmap)) { - // force our cahce32pixelref to be built - (void)cache->getCache32(); - bitmap->setInfo(SkImageInfo::MakeN32Premul(kCache32Count, 1)); - bitmap->setPixelRef(cache->getCache32PixelRef()); - + if (GradientBitmapType::kLegacy == bitmapType) { + // force our cache32pixelref to be built + (void)cache->getCache32(); + bitmap->setInfo(SkImageInfo::MakeN32Premul(kCache32Count, 1)); + bitmap->setPixelRef(cache->getCache32PixelRef()); + } else { + // For these cases we use the bitmap cache, but not the GradientShaderCache. So just + // allocate and populate the bitmap's data directly. + + SkImageInfo info; + switch (bitmapType) { + case GradientBitmapType::kSRGB: + info = SkImageInfo::Make(kCache32Count, 1, kRGBA_8888_SkColorType, + kPremul_SkAlphaType, + SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named)); + break; + case GradientBitmapType::kHalfFloat: + info = SkImageInfo::Make(kCache32Count, 1, kRGBA_F16_SkColorType, + kPremul_SkAlphaType, + SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named) + ->makeLinearGamma()); + break; + default: + SkFAIL("Unexpected bitmap type"); + return; + } + bitmap->allocPixels(info); + this->initLinearBitmap(bitmap); + } gCache->add(storage.get(), size, *bitmap); } } @@ -902,6 +984,7 @@ SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END #include "GrInvariantOutput.h" #include "GrTextureStripAtlas.h" #include "gl/GrGLContext.h" +#include "glsl/GrGLSLColorSpaceXformHelper.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" #include "glsl/GrGLSLProgramDataManager.h" #include "glsl/GrGLSLUniformHandler.h" @@ -1113,6 +1196,9 @@ void GrGradientEffect::GLSLProcessor::onSetData(const GrGLSLProgramDataManager& pdman.set1f(fFSYUni, yCoord); fCachedYCoord = yCoord; } + if (SkToBool(e.fColorSpaceXform)) { + pdman.setSkMatrix44(fColorSpaceXformUni, e.fColorSpaceXform->srcToDst()); + } break; } } @@ -1150,6 +1236,8 @@ uint32_t GrGradientEffect::GLSLProcessor::GenBaseGradientKey(const GrProcessor& } #endif + key |= GrColorSpaceXform::XformKey(e.fColorSpaceXform.get()) << kReservedBits; + return key; } @@ -1331,11 +1419,15 @@ void GrGradientEffect::GLSLProcessor::emitColor(GrGLSLFPFragmentBuilder* fragBui } case kTexture_ColorType: { + GrGLSLColorSpaceXformHelper colorSpaceHelper(uniformHandler, ge.fColorSpaceXform.get(), + &fColorSpaceXformUni); + const char* fsyuni = uniformHandler->getUniformCStr(fFSYUni); fragBuilder->codeAppendf("vec2 coord = vec2(%s, %s);", gradientTValue, fsyuni); fragBuilder->codeAppendf("%s = ", outputColor); - fragBuilder->appendTextureLookupAndModulate(inputColor, texSamplers[0], "coord"); + fragBuilder->appendTextureLookupAndModulate(inputColor, texSamplers[0], "coord", + kVec2f_GrSLType, &colorSpaceHelper); fragBuilder->codeAppend(";"); break; @@ -1351,12 +1443,12 @@ GrGradientEffect::GrGradientEffect(const CreateArgs& args) { fIsOpaque = shader.isOpaque(); fColorType = this->determineColorType(shader); + fColorSpaceXform = std::move(args.fColorSpaceXform); if (kTexture_ColorType != fColorType) { SkASSERT(shader.fOrigColors && shader.fOrigColors4f); if (args.fGammaCorrect) { fColors4f = SkTDArray<SkColor4f>(shader.fOrigColors4f, shader.fColorCount); - fColorSpaceXform = std::move(args.fColorSpaceXform); } else { fColors = SkTDArray<SkColor>(shader.fOrigColors, shader.fColorCount); } @@ -1397,8 +1489,22 @@ GrGradientEffect::GrGradientEffect(const CreateArgs& args) { // effect key. fPremulType = kBeforeInterp_PremulType; + SkGradientShaderBase::GradientBitmapType bitmapType = + SkGradientShaderBase::GradientBitmapType::kLegacy; + if (args.fGammaCorrect) { + // Try to use F16 if we can + if (args.fContext->caps()->isConfigTexturable(kRGBA_half_GrPixelConfig)) { + bitmapType = SkGradientShaderBase::GradientBitmapType::kHalfFloat; + } else if (args.fContext->caps()->isConfigTexturable(kSRGBA_8888_GrPixelConfig)) { + bitmapType = SkGradientShaderBase::GradientBitmapType::kSRGB; + } else { + // This should never happen, but just fall back to legacy behavior + SkDEBUGFAIL("Requesting a gamma-correct gradient FP without F16 or sRGB"); + } + } + SkBitmap bitmap; - shader.getGradientTableBitmap(&bitmap); + shader.getGradientTableBitmap(&bitmap, bitmapType); GrTextureStripAtlas::Desc desc; desc.fWidth = bitmap.width(); diff --git a/src/effects/gradients/SkGradientShaderPriv.h b/src/effects/gradients/SkGradientShaderPriv.h index aeb1301c7f..d8d50e8fa0 100644 --- a/src/effects/gradients/SkGradientShaderPriv.h +++ b/src/effects/gradients/SkGradientShaderPriv.h @@ -175,7 +175,13 @@ public: bool isOpaque() const override; - void getGradientTableBitmap(SkBitmap*) const; + enum class GradientBitmapType : uint8_t { + kLegacy, + kSRGB, + kHalfFloat, + }; + + void getGradientTableBitmap(SkBitmap*, GradientBitmapType bitmapType) const; enum { /// Seems like enough for visual accuracy. TODO: if pos[] deserves @@ -214,6 +220,9 @@ protected: bool onAsLuminanceColor(SkColor*) const override; + + void initLinearBitmap(SkBitmap* bitmap) const; + /* * Takes in pointers to gradient color and Rec info as colorSrc and recSrc respectively. * Count is the number of colors in the gradient @@ -490,6 +499,7 @@ private: SkScalar fCachedYCoord; GrGLSLProgramDataManager::UniformHandle fColorsUni; GrGLSLProgramDataManager::UniformHandle fFSYUni; + GrGLSLProgramDataManager::UniformHandle fColorSpaceXformUni; typedef GrGLSLFragmentProcessor INHERITED; }; |