Index: src/effects/gradients/SkGradientShader.cpp |
diff --git a/src/effects/gradients/SkGradientShader.cpp b/src/effects/gradients/SkGradientShader.cpp |
index 4f174d72b9f120f4bf967c6744b270cc4eb7e1a1..27f6136403a6186faea1c7e0444d6ff151b8e8e5 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(); |