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>
This commit is contained in:
parent
3d3d8841ff
commit
6667fb1239
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user