aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/shaders
diff options
context:
space:
mode:
authorGravatar Brian Osman <brianosman@google.com>2018-07-03 16:44:02 -0400
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2018-07-06 14:59:28 +0000
commit6667fb123911674835e093423fe2924983552653 (patch)
tree5a450c2626d087fc9f4a9af3be92e9aa23d14610 /src/shaders
parent3d3d8841ff86171a6667621880508ba499cf541f (diff)
Switch gradients to as-encoded blending
Stops are now always transformed all the way to destination color space, using a new helper struct. Even textured gradients on GPU pre-transform the stops, for simplicity and consistency. We plumb the destination config in to drive the choice of texture format - this is simpler and more correct. Bug: skia: Change-Id: Ie3aea9d29a8a5973a72551d9efeaf247ce29606b Reviewed-on: https://skia-review.googlesource.com/139173 Reviewed-by: Mike Klein <mtklein@google.com> Commit-Queue: Brian Osman <brianosman@google.com>
Diffstat (limited to 'src/shaders')
-rw-r--r--src/shaders/gradients/Sk4fGradientBase.cpp47
-rw-r--r--src/shaders/gradients/SkGradientShader.cpp167
-rw-r--r--src/shaders/gradients/SkGradientShaderPriv.h57
-rw-r--r--src/shaders/gradients/SkLinearGradient.cpp2
-rw-r--r--src/shaders/gradients/SkRadialGradient.cpp2
-rw-r--r--src/shaders/gradients/SkSweepGradient.cpp2
-rw-r--r--src/shaders/gradients/SkTwoPointConicalGradient.cpp2
-rw-r--r--src/shaders/gradients/SkTwoPointConicalGradient_gpu.cpp2
8 files changed, 130 insertions, 151 deletions
diff --git a/src/shaders/gradients/Sk4fGradientBase.cpp b/src/shaders/gradients/Sk4fGradientBase.cpp
index 19eabcfd91..0f6da52d1a 100644
--- a/src/shaders/gradients/Sk4fGradientBase.cpp
+++ b/src/shaders/gradients/Sk4fGradientBase.cpp
@@ -21,19 +21,19 @@ Sk4f pack_color(const SkColor4f& c4f, bool premul, const Sk4f& component_scale)
class IntervalIterator {
public:
- IntervalIterator(const SkGradientShaderBase& shader, SkColorSpace* dstCS, bool reverse)
+ IntervalIterator(const SkGradientShaderBase& shader, bool reverse)
: fShader(shader)
- , fDstCS(dstCS)
, fFirstPos(reverse ? SK_Scalar1 : 0)
, fBegin(reverse ? shader.fColorCount - 1 : 0)
, fAdvance(reverse ? -1 : 1) {
SkASSERT(shader.fColorCount > 0);
}
- void iterate(std::function<void(const SkColor4f&, const SkColor4f&,
+ void iterate(const SkColor4f* colors,
+ std::function<void(const SkColor4f&, const SkColor4f&,
SkScalar, SkScalar)> func) const {
if (!fShader.fOrigPos) {
- this->iterateImplicitPos(func);
+ this->iterateImplicitPos(colors, func);
return;
}
@@ -48,8 +48,7 @@ public:
const SkScalar currPos = fShader.fOrigPos[curr];
if (currPos != prevPos) {
SkASSERT((currPos - prevPos > 0) == (fAdvance > 0));
- func(fShader.getXformedColor(prev, fDstCS), fShader.getXformedColor(curr, fDstCS),
- prevPos, currPos);
+ func(colors[prev], colors[curr], prevPos, currPos);
}
prev = curr;
@@ -58,7 +57,8 @@ public:
}
private:
- void iterateImplicitPos(std::function<void(const SkColor4f&, const SkColor4f&,
+ void iterateImplicitPos(const SkColor4f* colors,
+ std::function<void(const SkColor4f&, const SkColor4f&,
SkScalar, SkScalar)> func) const {
// When clients don't provide explicit color stop positions (fPos == nullptr),
// the color stops are distributed evenly across the unit interval
@@ -73,33 +73,28 @@ private:
SkASSERT(curr >= 0 && curr < fShader.fColorCount);
const SkScalar currPos = prevPos + dt;
- func(fShader.getXformedColor(prev, fDstCS),
- fShader.getXformedColor(curr, fDstCS),
- prevPos, currPos);
+ func(colors[prev], colors[curr], prevPos, currPos);
prev = curr;
prevPos = currPos;
}
// emit the last interval with a pinned end position, to avoid precision issues
- func(fShader.getXformedColor(prev, fDstCS),
- fShader.getXformedColor(prev + fAdvance, fDstCS),
- prevPos, 1 - fFirstPos);
+ func(colors[prev], colors[prev + fAdvance], prevPos, 1 - fFirstPos);
}
const SkGradientShaderBase& fShader;
- SkColorSpace* fDstCS;
const SkScalar fFirstPos;
const int fBegin;
const int fAdvance;
};
void addMirrorIntervals(const SkGradientShaderBase& shader,
- SkColorSpace* dstCS,
+ const SkColor4f* colors,
const Sk4f& componentScale,
bool premulColors, bool reverse,
Sk4fGradientIntervalBuffer::BufferType* buffer) {
- const IntervalIterator iter(shader, dstCS, reverse);
- iter.iterate([&] (const SkColor4f& c0, const SkColor4f& c1, SkScalar t0, SkScalar t1) {
+ const IntervalIterator iter(shader, reverse);
+ iter.iterate(colors, [&] (const SkColor4f& c0, const SkColor4f& c1, SkScalar t0, SkScalar t1) {
SkASSERT(buffer->empty() || buffer->back().fT1 == 2 - t0);
const auto mirror_t0 = 2 - t0;
@@ -193,20 +188,25 @@ void Sk4fGradientIntervalBuffer::init(const SkGradientShaderBase& shader, SkColo
const SkScalar first_pos = reverse ? SK_Scalar1 : 0;
const SkScalar last_pos = SK_Scalar1 - first_pos;
+ // Transform all of the colors to destination color space
+ SkColor4fXformer xformedColors(shader.fOrigColors4f, count, shader.fColorSpace.get(), dstCS);
+
if (tileMode == SkShader::kClamp_TileMode) {
// synthetic edge interval: -/+inf .. P0
- const Sk4f clamp_color = pack_color(shader.getXformedColor(first_index, dstCS),
+ const Sk4f clamp_color = pack_color(xformedColors.fColors[first_index],
premulColors, componentScale);
const SkScalar clamp_pos = reverse ? SK_ScalarInfinity : SK_ScalarNegativeInfinity;
fIntervals.emplace_back(clamp_color, clamp_pos,
clamp_color, first_pos);
} else if (tileMode == SkShader::kMirror_TileMode && reverse) {
// synthetic mirror intervals injected before main intervals: (2 .. 1]
- addMirrorIntervals(shader, dstCS, componentScale, premulColors, false, &fIntervals);
+ addMirrorIntervals(shader, xformedColors.fColors, componentScale, premulColors, false,
+ &fIntervals);
}
- const IntervalIterator iter(shader, dstCS, reverse);
- iter.iterate([&] (const SkColor4f& c0, const SkColor4f& c1, SkScalar t0, SkScalar t1) {
+ const IntervalIterator iter(shader, reverse);
+ iter.iterate(xformedColors.fColors,
+ [&] (const SkColor4f& c0, const SkColor4f& c1, SkScalar t0, SkScalar t1) {
SkASSERT(fIntervals.empty() || fIntervals.back().fT1 == t0);
fIntervals.emplace_back(pack_color(c0, premulColors, componentScale), t0,
@@ -215,14 +215,15 @@ void Sk4fGradientIntervalBuffer::init(const SkGradientShaderBase& shader, SkColo
if (tileMode == SkShader::kClamp_TileMode) {
// synthetic edge interval: Pn .. +/-inf
- const Sk4f clamp_color = pack_color(shader.getXformedColor(last_index, dstCS),
+ const Sk4f clamp_color = pack_color(xformedColors.fColors[last_index],
premulColors, componentScale);
const SkScalar clamp_pos = reverse ? SK_ScalarNegativeInfinity : SK_ScalarInfinity;
fIntervals.emplace_back(clamp_color, last_pos,
clamp_color, clamp_pos);
} else if (tileMode == SkShader::kMirror_TileMode && !reverse) {
// synthetic mirror intervals injected after main intervals: [1 .. 2)
- addMirrorIntervals(shader, dstCS, componentScale, premulColors, true, &fIntervals);
+ addMirrorIntervals(shader, xformedColors.fColors, componentScale, premulColors, true,
+ &fIntervals);
}
}
diff --git a/src/shaders/gradients/SkGradientShader.cpp b/src/shaders/gradients/SkGradientShader.cpp
index cfcd335f42..fd97bc8925 100644
--- a/src/shaders/gradients/SkGradientShader.cpp
+++ b/src/shaders/gradients/SkGradientShader.cpp
@@ -22,7 +22,7 @@
#include "SkTwoPointConicalGradient.h"
#include "SkWriteBuffer.h"
#include "../../jumper/SkJumper.h"
-
+#include "../../third_party/skcms/skcms.h"
enum GradientSerializationFlags {
// Bits 29:31 used for various boolean flags
@@ -126,7 +126,7 @@ bool SkGradientShaderBase::DescriptorScope::unflatten(SkReadBuffer& buffer) {
SkGradientShaderBase::SkGradientShaderBase(const Descriptor& desc, const SkMatrix& ptsToUnit)
: INHERITED(desc.fLocalMatrix)
, fPtsToUnit(ptsToUnit)
- , fColorSpace(desc.fColorSpace ? desc.fColorSpace : SkColorSpace::MakeSRGBLinear())
+ , fColorSpace(desc.fColorSpace ? desc.fColorSpace : SkColorSpace::MakeSRGB())
, fColorsAreOpaque(true)
{
fPtsToUnit.getType(); // Precache so reads are threadsafe.
@@ -277,7 +277,6 @@ static void init_stop_pos(
bool SkGradientShaderBase::onAppendStages(const StageRec& rec) const {
SkRasterPipeline* p = rec.fPipeline;
SkArenaAlloc* alloc = rec.fAlloc;
- SkColorSpace* dstCS = rec.fDstCS;
SkJumper_DecalTileCtx* decal_ctx = nullptr;
SkMatrix matrix;
@@ -313,8 +312,12 @@ bool SkGradientShaderBase::onAppendStages(const StageRec& rec) const {
}
const bool premulGrad = fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag;
- auto prepareColor = [premulGrad, dstCS, this](int i) {
- SkColor4f c = this->getXformedColor(i, dstCS);
+
+ // Transform all of the colors to destination color space
+ SkColor4fXformer xformedColors(fOrigColors4f, fColorCount, fColorSpace.get(), rec.fDstCS);
+
+ auto prepareColor = [premulGrad, &xformedColors](int i) {
+ SkColor4f c = xformedColors.fColors[i];
return premulGrad ? c.premul()
: SkPM4f::From4f(Sk4f::Load(&c));
};
@@ -454,9 +457,37 @@ SkGradientShaderBase::AutoXformColors::AutoXformColors(const SkGradientShaderBas
xformer->apply(fColors.get(), origColors.get(), grad.fColorCount);
}
+SkColor4fXformer::SkColor4fXformer(const SkColor4f* colors, int colorCount,
+ SkColorSpace* src, SkColorSpace* dst) {
+ // Transform all of the colors to destination color space
+ fColors = colors;
+ if (!dst) {
+ return;
+ }
+
+ // Treat null sources as sRGB (safe because sRGB is a global singleton)
+ if (!src) {
+ src = SkColorSpace::MakeSRGB().get();
+ }
+
+ if (!SkColorSpace::Equals(src, dst)) {
+ skcms_ICCProfile srcProfile, dstProfile;
+ src->toProfile(&srcProfile);
+ dst->toProfile(&dstProfile);
+ fStorage.reset(colorCount);
+ const skcms_PixelFormat rgba_f32 = skcms_PixelFormat_RGBA_ffff;
+ const skcms_AlphaFormat unpremul = skcms_AlphaFormat_Unpremul;
+ SkAssertResult(skcms_Transform(colors, rgba_f32, unpremul, &srcProfile,
+ fStorage.begin(), rgba_f32, unpremul, &dstProfile,
+ colorCount));
+ fColors = fStorage.begin();
+ }
+}
+
static constexpr int kGradientTextureSize = 256;
-void SkGradientShaderBase::initLinearBitmap(SkBitmap* bitmap, GradientBitmapType bitmapType) const {
+void SkGradientShaderBase::initLinearBitmap(const SkColor4f* colors, SkBitmap* bitmap,
+ SkColorType colorType) const {
const bool interpInPremul = SkToBool(fGradFlags &
SkGradientShader::kInterpolateColorsInPremul_Flag);
SkHalf* pixelsF16 = reinterpret_cast<SkHalf*>(bitmap->getPixels());
@@ -471,26 +502,18 @@ void SkGradientShaderBase::initLinearBitmap(SkBitmap* bitmap, GradientBitmapType
pixelsF16[4*index+2] = c[2];
pixelsF16[4*index+3] = c[3];
};
- pixelWriteFn_t writeS32Pixel = [&](const Sk4f& c, int index) {
- pixels32[index] = Sk4f_toS32(c);
- };
- pixelWriteFn_t writeL32Pixel = [&](const Sk4f& c, int index) {
+ pixelWriteFn_t write8888Pixel = [&](const Sk4f& c, int index) {
pixels32[index] = Sk4f_toL32(c);
};
pixelWriteFn_t writeSizedPixel =
- (bitmapType == GradientBitmapType::kHalfFloat) ? writeF16Pixel :
- (bitmapType == GradientBitmapType::kSRGB ) ? writeS32Pixel : writeL32Pixel;
+ (colorType == kRGBA_F16_SkColorType) ? writeF16Pixel : write8888Pixel;
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;
- // When not in legacy mode, we just want the original 4f colors - so we pass in
- // our own CS for identity/no transform.
- auto* cs = bitmapType != GradientBitmapType::kLegacy ? fColorSpace.get() : nullptr;
-
int prevIndex = 0;
for (int i = 1; i < fColorCount; i++) {
// Historically, stops have been mapped to [0, 256], with 256 then nudged to the
@@ -500,10 +523,8 @@ void SkGradientShaderBase::initLinearBitmap(SkBitmap* bitmap, GradientBitmapType
SkIntToScalar(kGradientTextureSize - 1));
if (nextIndex > prevIndex) {
- SkColor4f color0 = this->getXformedColor(i - 1, cs),
- color1 = this->getXformedColor(i , cs);
- Sk4f c0 = Sk4f::Load(color0.vec()),
- c1 = Sk4f::Load(color1.vec());
+ Sk4f c0 = Sk4f::Load(colors[i - 1].vec()),
+ c1 = Sk4f::Load(colors[i ].vec());
if (interpInPremul) {
c0 = c0 * Sk4f(c0[3], c0[3], c0[3], 1.0f);
@@ -523,18 +544,6 @@ void SkGradientShaderBase::initLinearBitmap(SkBitmap* bitmap, GradientBitmapType
SkASSERT(prevIndex == kGradientTextureSize - 1);
}
-SkColor4f SkGradientShaderBase::getXformedColor(size_t i, SkColorSpace* dstCS) const {
- if (dstCS) {
- return to_colorspace(fOrigColors4f[i], fColorSpace.get(), dstCS);
- }
-
- // Legacy/srgb color.
- // We quantize upfront to ensure stable SkColor round-trips.
- auto rgb255 = sk_linear_to_srgb(Sk4f::Load(fOrigColors4f[i].vec()));
- auto rgb = SkNx_cast<float>(rgb255) * (1/255.0f);
- return { rgb[0], rgb[1], rgb[2], fOrigColors4f[i].fA };
-}
-
SK_DECLARE_STATIC_MUTEX(gGradientCacheMutex);
/*
* Because our caller might rebuild the same (logically the same) gradient
@@ -543,8 +552,8 @@ SK_DECLARE_STATIC_MUTEX(gGradientCacheMutex);
* To do that, we maintain a private cache of built-bitmaps, based on our
* colors and positions.
*/
-void SkGradientShaderBase::getGradientTableBitmap(SkBitmap* bitmap,
- GradientBitmapType bitmapType) const {
+void SkGradientShaderBase::getGradientTableBitmap(const SkColor4f* colors, SkBitmap* bitmap,
+ SkColorType colorType) const {
// build our key: [numColors + colors[] + {positions[]} + flags + colorType ]
static_assert(sizeof(SkColor4f) % sizeof(int32_t) == 0, "");
const int colorsAsIntCount = fColorCount * sizeof(SkColor4f) / sizeof(int32_t);
@@ -557,7 +566,7 @@ void SkGradientShaderBase::getGradientTableBitmap(SkBitmap* bitmap,
int32_t* buffer = storage.get();
*buffer++ = fColorCount;
- memcpy(buffer, fOrigColors4f, fColorCount * sizeof(SkColor4f));
+ memcpy(buffer, colors, fColorCount * sizeof(SkColor4f));
buffer += colorsAsIntCount;
if (fColorCount > 2) {
for (int i = 1; i < fColorCount; i++) {
@@ -565,13 +574,13 @@ void SkGradientShaderBase::getGradientTableBitmap(SkBitmap* bitmap,
}
}
*buffer++ = fGradFlags;
- *buffer++ = static_cast<int32_t>(bitmapType);
+ *buffer++ = static_cast<int32_t>(colorType);
SkASSERT(buffer - storage.get() == count);
///////////////////////////////////
static SkGradientBitmapCache* gCache;
- // each cache cost 1K or 2K of RAM, since each bitmap will be 1x256 at either 32bpp or 64bpp
+ // Each cache entry costs 1K or 2K of RAM. Each bitmap will be 1x256 at either 32bpp or 64bpp.
static const int MAX_NUM_CACHED_GRADIENT_BITMAPS = 32;
SkAutoMutexAcquire ama(gGradientCacheMutex);
@@ -581,27 +590,10 @@ void SkGradientShaderBase::getGradientTableBitmap(SkBitmap* bitmap,
size_t size = count * sizeof(int32_t);
if (!gCache->find(storage.get(), size, bitmap)) {
- // 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::kLegacy:
- info = SkImageInfo::Make(kGradientTextureSize, 1, kRGBA_8888_SkColorType,
- kPremul_SkAlphaType);
- break;
- case GradientBitmapType::kSRGB:
- info = SkImageInfo::Make(kGradientTextureSize, 1, kRGBA_8888_SkColorType,
- kPremul_SkAlphaType, SkColorSpace::MakeSRGB());
- break;
- case GradientBitmapType::kHalfFloat:
- info = SkImageInfo::Make(kGradientTextureSize, 1, kRGBA_F16_SkColorType,
- kPremul_SkAlphaType, SkColorSpace::MakeSRGBLinear());
- break;
- }
-
+ SkImageInfo info = SkImageInfo::Make(kGradientTextureSize, 1, colorType,
+ kPremul_SkAlphaType);
bitmap->allocPixels(info);
- this->initLinearBitmap(bitmap, bitmapType);
+ this->initLinearBitmap(colors, bitmap, colorType);
bitmap->setImmutable();
gCache->add(storage.get(), size, *bitmap);
}
@@ -709,8 +701,13 @@ struct ColorStopOptimizer {
struct ColorConverter {
ColorConverter(const SkColor* colors, int count) {
+ const float ONE_OVER_255 = 1.f / 255;
for (int i = 0; i < count; ++i) {
- fColors4f.push_back(SkColor4f::FromColor(colors[i]));
+ fColors4f.push_back({
+ SkColorGetR(colors[i]) * ONE_OVER_255,
+ SkColorGetG(colors[i]) * ONE_OVER_255,
+ SkColorGetB(colors[i]) * ONE_OVER_255,
+ SkColorGetA(colors[i]) * ONE_OVER_255 });
}
}
@@ -1127,11 +1124,11 @@ inline GrFragmentProcessor::OptimizationFlags GrGradientEffect::OptFlags(bool is
: kCompatibleWithCoverageAsAlpha_OptimizationFlag;
}
-void GrGradientEffect::addInterval(const SkGradientShaderBase& shader, size_t idx0, size_t idx1,
- SkColorSpace* dstCS) {
+void GrGradientEffect::addInterval(const SkGradientShaderBase& shader, const SkColor4f* colors,
+ size_t idx0, size_t idx1) {
SkASSERT(idx0 <= idx1);
- const auto c4f0 = shader.getXformedColor(idx0, dstCS),
- c4f1 = shader.getXformedColor(idx1, dstCS);
+ const auto c4f0 = colors[idx0],
+ c4f1 = colors[idx1];
const auto c0 = (fPremulType == kBeforeInterp_PremulType)
? c4f0.premul().to4f() : Sk4f::Load(c4f0.vec()),
c1 = (fPremulType == kBeforeInterp_PremulType)
@@ -1163,12 +1160,16 @@ GrGradientEffect::GrGradientEffect(ClassID classID, const CreateArgs& args, bool
fPremulType = (args.fShader->getGradFlags() & SkGradientShader::kInterpolateColorsInPremul_Flag)
? kBeforeInterp_PremulType : kAfterInterp_PremulType;
+ // Transform all of the colors to destination color space
+ SkColor4fXformer xformedColors(shader.fOrigColors4f, shader.fColorCount,
+ shader.fColorSpace.get(), args.fDstColorSpaceInfo->colorSpace());
+
// First, determine the interpolation strategy and params.
switch (shader.fColorCount) {
case 2:
SkASSERT(!shader.fOrigPos);
fStrategy = InterpolationStrategy::kSingle;
- this->addInterval(shader, 0, 1, args.fDstColorSpace);
+ this->addInterval(shader, xformedColors.fColors, 0, 1);
break;
case 3:
fThreshold = shader.getPos(1);
@@ -1181,22 +1182,22 @@ GrGradientEffect::GrGradientEffect(ClassID classID, const CreateArgs& args, bool
if (fWrapMode == GrSamplerState::WrapMode::kClamp) {
fStrategy = InterpolationStrategy::kThresholdClamp1;
// Clamp interval (scale == 0, bias == colors[0]).
- this->addInterval(shader, 0, 0, args.fDstColorSpace);
+ this->addInterval(shader, xformedColors.fColors, 0, 0);
} else {
// We can ignore the hard stop when not clamping.
fStrategy = InterpolationStrategy::kSingle;
}
- this->addInterval(shader, 1, 2, args.fDstColorSpace);
+ this->addInterval(shader, xformedColors.fColors, 1, 2);
break;
}
if (SkScalarNearlyEqual(shader.fOrigPos[1], 1)) {
// hard stop on the right edge.
- this->addInterval(shader, 0, 1, args.fDstColorSpace);
+ this->addInterval(shader, xformedColors.fColors, 0, 1);
if (fWrapMode == GrSamplerState::WrapMode::kClamp) {
fStrategy = InterpolationStrategy::kThresholdClamp0;
// Clamp interval (scale == 0, bias == colors[2]).
- this->addInterval(shader, 2, 2, args.fDstColorSpace);
+ this->addInterval(shader, xformedColors.fColors, 2, 2);
} else {
// We can ignore the hard stop when not clamping.
fStrategy = InterpolationStrategy::kSingle;
@@ -1207,8 +1208,8 @@ GrGradientEffect::GrGradientEffect(ClassID classID, const CreateArgs& args, bool
// Two arbitrary interpolation intervals.
fStrategy = InterpolationStrategy::kThreshold;
- this->addInterval(shader, 0, 1, args.fDstColorSpace);
- this->addInterval(shader, 1, 2, args.fDstColorSpace);
+ this->addInterval(shader, xformedColors.fColors, 0, 1);
+ this->addInterval(shader, xformedColors.fColors, 1, 2);
break;
case 4:
if (shader.fOrigPos && SkScalarNearlyEqual(shader.fOrigPos[1], shader.fOrigPos[2])) {
@@ -1218,8 +1219,8 @@ GrGradientEffect::GrGradientEffect(ClassID classID, const CreateArgs& args, bool
// Single hard stop => two arbitrary interpolation intervals.
fStrategy = InterpolationStrategy::kThreshold;
fThreshold = shader.getPos(1);
- this->addInterval(shader, 0, 1, args.fDstColorSpace);
- this->addInterval(shader, 2, 3, args.fDstColorSpace);
+ this->addInterval(shader, xformedColors.fColors, 0, 1);
+ this->addInterval(shader, xformedColors.fColors, 2, 3);
}
break;
default:
@@ -1231,23 +1232,16 @@ GrGradientEffect::GrGradientEffect(ClassID classID, const CreateArgs& args, bool
// Analytical cases.
fCoordTransform.reset(*args.fMatrix);
} else {
- SkGradientShaderBase::GradientBitmapType bitmapType =
- SkGradientShaderBase::GradientBitmapType::kLegacy;
- auto caps = args.fContext->contextPriv().caps();
- if (args.fDstColorSpace) {
- // Try to use F16 if we can
- if (caps->isConfigTexturable(kRGBA_half_GrPixelConfig)) {
- bitmapType = SkGradientShaderBase::GradientBitmapType::kHalfFloat;
- } else if (caps->isConfigTexturable(kSRGBA_8888_GrPixelConfig)) {
- bitmapType = SkGradientShaderBase::GradientBitmapType::kSRGB;
- } else {
- // This can happen, but only if someone explicitly creates an unsupported
- // (eg sRGB) surface. Just fall back to legacy behavior.
- }
+ // Use 8888 or F16, depending on the destination config.
+ // TODO: Use 1010102 for opaque gradients, at least if destination is 1010102?
+ SkColorType colorType = kRGBA_8888_SkColorType;
+ if (kLow_GrSLPrecision != GrSLSamplerPrecision(args.fDstColorSpaceInfo->config()) &&
+ args.fContext->contextPriv().caps()->isConfigTexturable(kRGBA_half_GrPixelConfig)) {
+ colorType = kRGBA_F16_SkColorType;
}
SkBitmap bitmap;
- shader.getGradientTableBitmap(&bitmap, bitmapType);
+ shader.getGradientTableBitmap(xformedColors.fColors, &bitmap, colorType);
SkASSERT(1 == bitmap.height() && SkIsPow2(bitmap.width()));
auto atlasManager = args.fContext->contextPriv().textureStripAtlasManager();
@@ -1371,9 +1365,6 @@ GrGradientEffect::RandomGradientParams::RandomGradientParams(SkRandom* random) {
// if using SkColor4f, attach a random (possibly null) color space (with linear gamma)
if (fUseColors4f) {
fColorSpace = GrTest::TestColorSpace(random);
- if (fColorSpace) {
- fColorSpace = fColorSpace->makeLinearGamma();
- }
}
SkScalar stop = 0.f;
diff --git a/src/shaders/gradients/SkGradientShaderPriv.h b/src/shaders/gradients/SkGradientShaderPriv.h
index 9a565a8d4a..0f3e73603a 100644
--- a/src/shaders/gradients/SkGradientShaderPriv.h
+++ b/src/shaders/gradients/SkGradientShaderPriv.h
@@ -12,6 +12,7 @@
#include "SkArenaAlloc.h"
#include "SkMatrix.h"
+#include "SkPM4fPriv.h"
#include "SkShaderBase.h"
#include "SkTArray.h"
#include "SkTemplates.h"
@@ -63,18 +64,10 @@ public:
bool isOpaque() const override;
- enum class GradientBitmapType : uint8_t {
- kLegacy,
- kSRGB,
- kHalfFloat,
- };
-
- void getGradientTableBitmap(SkBitmap*, GradientBitmapType bitmapType) const;
+ void getGradientTableBitmap(const SkColor4f* colors, SkBitmap*, SkColorType) const;
uint32_t getGradFlags() const { return fGradFlags; }
- SkColor4f getXformedColor(size_t index, SkColorSpace*) const;
-
protected:
class GradientShaderBase4fContext;
@@ -85,7 +78,7 @@ protected:
bool onAsLuminanceColor(SkColor*) const override;
- void initLinearBitmap(SkBitmap* bitmap, GradientBitmapType) const;
+ void initLinearBitmap(const SkColor4f* colors, SkBitmap* bitmap, SkColorType colorType) const;
bool onAppendStages(const StageRec&) const override;
@@ -119,7 +112,7 @@ public:
SkColor getLegacyColor(int i) const {
SkASSERT(i < fColorCount);
- return fOrigColors4f[i].toSkColor();
+ return Sk4f_toL32(swizzle_rb(Sk4f::Load(fOrigColors4f[i].vec())));
}
SkColor4f* fOrigColors4f; // original colors, as linear floats
@@ -145,6 +138,15 @@ private:
///////////////////////////////////////////////////////////////////////////////
+struct SkColor4fXformer {
+ SkColor4fXformer(const SkColor4f* colors, int colorCount, SkColorSpace* src, SkColorSpace* dst);
+
+ const SkColor4f* fColors;
+ SkSTArray<4, SkColor4f, true> fStorage;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
#if SK_SUPPORT_GPU
#include "GrColorSpaceInfo.h"
@@ -188,11 +190,11 @@ public:
const SkGradientShaderBase* shader,
const SkMatrix* matrix,
SkShader::TileMode tileMode,
- SkColorSpace* dstColorSpace)
+ const GrColorSpaceInfo* dstColorSpaceInfo)
: fContext(context)
, fShader(shader)
, fMatrix(matrix)
- , fDstColorSpace(dstColorSpace) {
+ , fDstColorSpaceInfo(dstColorSpaceInfo) {
switch (tileMode) {
case SkShader::kClamp_TileMode:
fWrapMode = GrSamplerState::WrapMode::kClamp;
@@ -214,18 +216,18 @@ public:
const SkGradientShaderBase* shader,
const SkMatrix* matrix,
GrSamplerState::WrapMode wrapMode,
- SkColorSpace* dstColorSpace)
+ const GrColorSpaceInfo* dstColorSpaceInfo)
: fContext(context)
, fShader(shader)
, fMatrix(matrix)
, fWrapMode(wrapMode)
- , fDstColorSpace(dstColorSpace) {}
+ , fDstColorSpaceInfo(dstColorSpaceInfo) {}
GrContext* fContext;
const SkGradientShaderBase* fShader;
const SkMatrix* fMatrix;
GrSamplerState::WrapMode fWrapMode;
- SkColorSpace* fDstColorSpace;
+ const GrColorSpaceInfo* fDstColorSpaceInfo;
};
class GLSLProcessor;
@@ -255,29 +257,13 @@ protected:
void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
- // Helper function used by derived class factories to handle color space transformation and
- // modulation by input alpha.
+ // Helper function used by derived class factories to handle modulation by input alpha.
static std::unique_ptr<GrFragmentProcessor> AdjustFP(
std::unique_ptr<GrGradientEffect> gradientFP, const CreateArgs& args) {
if (!gradientFP->isValid()) {
return nullptr;
}
- std::unique_ptr<GrFragmentProcessor> fp;
- // With analytic gradients, we pre-convert the stops to the destination color space, so no
- // xform is needed. With texture-based gradients, we leave the data in the source color
- // space (to avoid clamping if we can't use F16)... Add an extra FP to do the xform.
- if (gradientFP->fStrategy == InterpolationStrategy::kTexture) {
- // Our texture is always either F16 or sRGB, so the data is "linear" in the shader.
- // Create our xform assuming float inputs, which will suppress any extra sRGB work.
- // We do support having a transfer function on the color space of the stops, so
- // this FP may include that transformation.
- fp = GrColorSpaceXformEffect::Make(std::move(gradientFP),
- args.fShader->fColorSpace.get(),
- args.fDstColorSpace);
- } else {
- fp = std::move(gradientFP);
- }
- return GrFragmentProcessor::MulChildByInputAlpha(std::move(fp));
+ return GrFragmentProcessor::MulChildByInputAlpha(std::move(gradientFP));
}
#if GR_TEST_UTILS
@@ -313,7 +299,8 @@ protected:
}
private:
- void addInterval(const SkGradientShaderBase&, size_t idx0, size_t idx1, SkColorSpace*);
+ void addInterval(const SkGradientShaderBase&, const SkColor4f* colors,
+ size_t idx0, size_t idx1);
static OptimizationFlags OptFlags(bool isOpaque);
diff --git a/src/shaders/gradients/SkLinearGradient.cpp b/src/shaders/gradients/SkLinearGradient.cpp
index 34cac269f5..9ad768b270 100644
--- a/src/shaders/gradients/SkLinearGradient.cpp
+++ b/src/shaders/gradients/SkLinearGradient.cpp
@@ -201,7 +201,7 @@ std::unique_ptr<GrFragmentProcessor> SkLinearGradient::asFragmentProcessor(
matrix.postConcat(fPtsToUnit);
return GrLinearGradient::Make(GrGradientEffect::CreateArgs(
- args.fContext, this, &matrix, fTileMode, args.fDstColorSpaceInfo->colorSpace()));
+ args.fContext, this, &matrix, fTileMode, args.fDstColorSpaceInfo));
}
diff --git a/src/shaders/gradients/SkRadialGradient.cpp b/src/shaders/gradients/SkRadialGradient.cpp
index ec0f8a0f95..70edb65c6d 100644
--- a/src/shaders/gradients/SkRadialGradient.cpp
+++ b/src/shaders/gradients/SkRadialGradient.cpp
@@ -169,7 +169,7 @@ std::unique_ptr<GrFragmentProcessor> SkRadialGradient::asFragmentProcessor(
matrix.postConcat(fPtsToUnit);
return GrRadialGradient::Make(GrGradientEffect::CreateArgs(
- args.fContext, this, &matrix, fTileMode, args.fDstColorSpaceInfo->colorSpace()));
+ args.fContext, this, &matrix, fTileMode, args.fDstColorSpaceInfo));
}
#endif
diff --git a/src/shaders/gradients/SkSweepGradient.cpp b/src/shaders/gradients/SkSweepGradient.cpp
index cfce5fe025..89c5cb162c 100644
--- a/src/shaders/gradients/SkSweepGradient.cpp
+++ b/src/shaders/gradients/SkSweepGradient.cpp
@@ -224,7 +224,7 @@ std::unique_ptr<GrFragmentProcessor> SkSweepGradient::asFragmentProcessor(
return GrSweepGradient::Make(
GrGradientEffect::CreateArgs(args.fContext, this, &matrix, fTileMode,
- args.fDstColorSpaceInfo->colorSpace()),
+ args.fDstColorSpaceInfo),
fTBias, fTScale);
}
diff --git a/src/shaders/gradients/SkTwoPointConicalGradient.cpp b/src/shaders/gradients/SkTwoPointConicalGradient.cpp
index 5739a8003a..1bc6a9b8bc 100644
--- a/src/shaders/gradients/SkTwoPointConicalGradient.cpp
+++ b/src/shaders/gradients/SkTwoPointConicalGradient.cpp
@@ -190,7 +190,7 @@ std::unique_ptr<GrFragmentProcessor> SkTwoPointConicalGradient::asFragmentProces
return Gr2PtConicalGradientEffect::Make(
GrGradientEffect::CreateArgs(args.fContext, this, &matrix, fTileMode,
- args.fDstColorSpaceInfo->colorSpace()));
+ args.fDstColorSpaceInfo));
}
#endif
diff --git a/src/shaders/gradients/SkTwoPointConicalGradient_gpu.cpp b/src/shaders/gradients/SkTwoPointConicalGradient_gpu.cpp
index 9d447785ed..c346f35327 100644
--- a/src/shaders/gradients/SkTwoPointConicalGradient_gpu.cpp
+++ b/src/shaders/gradients/SkTwoPointConicalGradient_gpu.cpp
@@ -390,7 +390,7 @@ std::unique_ptr<GrFragmentProcessor> Gr2PtConicalGradientEffect::Make(
SkMatrix matrix = *args.fMatrix;
GrGradientEffect::CreateArgs newArgs(args.fContext, args.fShader, &matrix, args.fWrapMode,
- args.fDstColorSpace);
+ args.fDstColorSpaceInfo);
// Data and matrix has to be prepared before constructing TwoPointConicalEffect so its parent
// class can have the right matrix to work with during construction.
TwoPointConicalEffect::Data data(shader, matrix);