aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/effects
diff options
context:
space:
mode:
Diffstat (limited to 'src/effects')
-rw-r--r--src/effects/gradients/SkGradientShader.cpp134
-rw-r--r--src/effects/gradients/SkGradientShaderPriv.h12
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;
};