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:
Brian Osman 2018-07-03 16:44:02 -04:00 committed by Skia Commit-Bot
parent 3d3d8841ff
commit 6667fb1239
8 changed files with 130 additions and 151 deletions

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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);

View File

@ -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));
}

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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);