aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--include/gpu/GrColorSpaceXform.h4
-rw-r--r--src/effects/gradients/SkGradientShader.cpp133
-rw-r--r--src/effects/gradients/SkGradientShaderPriv.h47
-rw-r--r--src/effects/gradients/SkLinearGradient.cpp6
-rw-r--r--src/effects/gradients/SkRadialGradient.cpp5
-rw-r--r--src/effects/gradients/SkSweepGradient.cpp5
-rw-r--r--src/effects/gradients/SkTwoPointConicalGradient.cpp5
-rw-r--r--src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp3
-rw-r--r--src/gpu/GrColorSpaceXform.cpp12
9 files changed, 192 insertions, 28 deletions
diff --git a/include/gpu/GrColorSpaceXform.h b/include/gpu/GrColorSpaceXform.h
index 2d2cc867a5..7c88c62850 100644
--- a/include/gpu/GrColorSpaceXform.h
+++ b/include/gpu/GrColorSpaceXform.h
@@ -23,7 +23,7 @@ public:
static sk_sp<GrColorSpaceXform> Make(SkColorSpace* src, SkColorSpace* dst);
- const SkMatrix44& srcToDst() { return fSrcToDst; }
+ const SkMatrix44& srcToDst() const { return fSrcToDst; }
/**
* GrGLSLFragmentProcessor::GenKey() must call this and include the returned value in its
@@ -34,6 +34,8 @@ public:
return SkToBool(xform) ? 1 : 0;
}
+ static bool Equals(const GrColorSpaceXform* a, const GrColorSpaceXform* b);
+
GrColor4f apply(const GrColor4f& srcColor);
private:
diff --git a/src/effects/gradients/SkGradientShader.cpp b/src/effects/gradients/SkGradientShader.cpp
index 8f07419da5..749d1751e2 100644
--- a/src/effects/gradients/SkGradientShader.cpp
+++ b/src/effects/gradients/SkGradientShader.cpp
@@ -14,6 +14,7 @@
void SkGradientShaderBase::Descriptor::flatten(SkWriteBuffer& buffer) const {
buffer.writeColorArray(fColors, fCount);
+ // TODO: Flatten fColors4f and fColorSpace
if (fPos) {
buffer.writeBool(true);
buffer.writeScalarArray(fPos, fCount);
@@ -31,6 +32,7 @@ void SkGradientShaderBase::Descriptor::flatten(SkWriteBuffer& buffer) const {
}
bool SkGradientShaderBase::DescriptorScope::unflatten(SkReadBuffer& buffer) {
+ // TODO: Unflatten fColors4f and fColorSpace
fCount = buffer.getArrayCount();
if (fCount > kStorageCount) {
size_t allocSize = (sizeof(SkColor) + sizeof(SkScalar)) * fCount;
@@ -103,7 +105,7 @@ SkGradientShaderBase::SkGradientShaderBase(const Descriptor& desc, const SkMatri
}
if (fColorCount > kColorStorageCount) {
- size_t size = sizeof(SkColor) + sizeof(Rec);
+ size_t size = sizeof(SkColor) + sizeof(SkColor4f) + sizeof(Rec);
if (desc.fPos) {
size += sizeof(SkScalar);
}
@@ -114,8 +116,14 @@ SkGradientShaderBase::SkGradientShaderBase(const Descriptor& desc, const SkMatri
fOrigColors = fStorage;
}
- // Now copy over the colors, adding the dummies as needed
- {
+ fOrigColors4f = (SkColor4f*)(fOrigColors + fColorCount);
+
+ // We should have been supplied with either fColors *or* (fColors4f and fColorSpace)
+ if (desc.fColors) {
+ // TODO: Should we support alternate gamma-encoded colorspaces with SkColor inputs?
+ SkASSERT(!desc.fColors4f && !desc.fColorSpace);
+
+ // Now copy over the colors, adding the dummies as needed
SkColor* origColors = fOrigColors;
if (dummyFirst) {
*origColors++ = desc.fColors[0];
@@ -125,14 +133,44 @@ SkGradientShaderBase::SkGradientShaderBase(const Descriptor& desc, const SkMatri
origColors += desc.fCount;
*origColors = desc.fColors[desc.fCount - 1];
}
+
+ // Convert our SkColor colors to SkColor4f as well
+ for (int i = 0; i < fColorCount; ++i) {
+ fOrigColors4f[i] = SkColor4f::FromColor(fOrigColors[i]);
+ }
+
+ // Color space refers to fColors4f, so it's always linear gamma
+ fColorSpace = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named)->makeLinearGamma();
+ } else {
+ SkASSERT(desc.fColors4f && desc.fColorSpace && desc.fColorSpace->gammaIsLinear());
+
+ // Now copy over the colors, adding the dummies as needed
+ SkColor4f* origColors = fOrigColors4f;
+ if (dummyFirst) {
+ *origColors++ = desc.fColors4f[0];
+ }
+ memcpy(origColors, desc.fColors4f, desc.fCount * sizeof(SkColor4f));
+ if (dummyLast) {
+ origColors += desc.fCount;
+ *origColors = desc.fColors4f[desc.fCount - 1];
+ }
+
+ // Convert our SkColor4f colors to SkColor as well. Note that this is incorrect if the
+ // source colors are not in sRGB gamut. We would need to do a gamut transformation, but
+ // SkColorSpaceXform can't do that (yet). GrColorSpaceXform can, but we may not have GPU
+ // support compiled in here.
+ for (int i = 0; i < fColorCount; ++i) {
+ fOrigColors[i] = fOrigColors4f[i].toSkColor();
+ }
+ fColorSpace = desc.fColorSpace;
}
if (desc.fPos && fColorCount) {
- fOrigPos = (SkScalar*)(fOrigColors + fColorCount);
+ fOrigPos = (SkScalar*)(fOrigColors4f + fColorCount);
fRecs = (Rec*)(fOrigPos + fColorCount);
} else {
fOrigPos = nullptr;
- fRecs = (Rec*)(fOrigColors + fColorCount);
+ fRecs = (Rec*)(fOrigColors4f + fColorCount);
}
if (fColorCount > 2) {
@@ -218,6 +256,8 @@ void SkGradientShaderBase::initCommon() {
void SkGradientShaderBase::flatten(SkWriteBuffer& buffer) const {
Descriptor desc;
desc.fColors = fOrigColors;
+ desc.fColors4f = fOrigColors4f;
+ desc.fColorSpace = fColorSpace;
desc.fPos = fOrigPos;
desc.fCount = fColorCount;
desc.fTileMode = fTileMode;
@@ -661,6 +701,8 @@ static void desc_init(SkGradientShaderBase::Descriptor* desc,
SkASSERT(colorCount > 1);
desc->fColors = colors;
+ desc->fColors4f = nullptr;
+ desc->fColorSpace = nullptr; // SkColor is always sRGB
desc->fPos = pos;
desc->fCount = colorCount;
desc->fTileMode = mode;
@@ -945,6 +987,52 @@ void GrGradientEffect::GLSLProcessor::emitUniforms(GrGLSLUniformHandler* uniform
}
}
+static inline void set_after_interp_color_uni_array(
+ const GrGLSLProgramDataManager& pdman,
+ const GrGLSLProgramDataManager::UniformHandle uni,
+ const SkTDArray<SkColor4f>& colors,
+ const GrColorSpaceXform* colorSpaceXform) {
+ int count = colors.count();
+ if (colorSpaceXform) {
+ constexpr int kSmallCount = 10;
+ SkAutoSTArray<4 * kSmallCount, float> vals(4 * count);
+
+ for (int i = 0; i < count; i++) {
+ colorSpaceXform->srcToDst().mapScalars(colors[i].vec(), &vals[4 * i]);
+ }
+
+ pdman.set4fv(uni, count, vals.get());
+ } else {
+ pdman.set4fv(uni, count, (float*)&colors[0]);
+ }
+}
+
+static inline void set_before_interp_color_uni_array(
+ const GrGLSLProgramDataManager& pdman,
+ const GrGLSLProgramDataManager::UniformHandle uni,
+ const SkTDArray<SkColor4f>& colors,
+ const GrColorSpaceXform* colorSpaceXform) {
+ int count = colors.count();
+ constexpr int kSmallCount = 10;
+ SkAutoSTArray<4 * kSmallCount, float> vals(4 * count);
+
+ for (int i = 0; i < count; i++) {
+ float a = colors[i].fA;
+ vals[4 * i + 0] = colors[i].fR * a;
+ vals[4 * i + 1] = colors[i].fG * a;
+ vals[4 * i + 2] = colors[i].fB * a;
+ vals[4 * i + 3] = a;
+ }
+
+ if (colorSpaceXform) {
+ for (int i = 0; i < count; i++) {
+ colorSpaceXform->srcToDst().mapScalars(&vals[4 * i]);
+ }
+ }
+
+ pdman.set4fv(uni, count, vals.get());
+}
+
static inline void set_after_interp_color_uni_array(const GrGLSLProgramDataManager& pdman,
const GrGLSLProgramDataManager::UniformHandle uni,
const SkTDArray<SkColor>& colors) {
@@ -998,10 +1086,22 @@ void GrGradientEffect::GLSLProcessor::onSetData(const GrGLSLProgramDataManager&
#endif
case GrGradientEffect::kTwo_ColorType:
case GrGradientEffect::kThree_ColorType: {
- if (GrGradientEffect::kBeforeInterp_PremulType == e.getPremulType()) {
- set_before_interp_color_uni_array(pdman, fColorsUni, e.fColors);
+ if (e.fColors4f.count() > 0) {
+ // Gamma-correct / color-space aware
+ if (GrGradientEffect::kBeforeInterp_PremulType == e.getPremulType()) {
+ set_before_interp_color_uni_array(pdman, fColorsUni, e.fColors4f,
+ e.fColorSpaceXform.get());
+ } else {
+ set_after_interp_color_uni_array(pdman, fColorsUni, e.fColors4f,
+ e.fColorSpaceXform.get());
+ }
} else {
- set_after_interp_color_uni_array(pdman, fColorsUni, e.fColors);
+ // Legacy mode. Would be nice if we had converted the 8-bit colors to float earlier
+ if (GrGradientEffect::kBeforeInterp_PremulType == e.getPremulType()) {
+ set_before_interp_color_uni_array(pdman, fColorsUni, e.fColors);
+ } else {
+ set_after_interp_color_uni_array(pdman, fColorsUni, e.fColors);
+ }
}
break;
@@ -1253,7 +1353,11 @@ GrGradientEffect::GrGradientEffect(const CreateArgs& args) {
fColorType = this->determineColorType(shader);
if (kTexture_ColorType != fColorType) {
- if (shader.fOrigColors) {
+ 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);
}
@@ -1353,7 +1457,8 @@ bool GrGradientEffect::onIsEqual(const GrFragmentProcessor& processor) const {
}
} else {
if (this->getPremulType() != ge.getPremulType() ||
- this->fColors.count() != ge.fColors.count()) {
+ this->fColors.count() != ge.fColors.count() ||
+ this->fColors4f.count() != ge.fColors4f.count()) {
return false;
}
@@ -1362,10 +1467,16 @@ bool GrGradientEffect::onIsEqual(const GrFragmentProcessor& processor) const {
return false;
}
}
+ for (int i = 0; i < this->fColors4f.count(); i++) {
+ if (*this->getColors4f(i) != *ge.getColors4f(i)) {
+ return false;
+ }
+ }
}
+
SkASSERT(this->useAtlas() == ge.useAtlas());
- return true;
+ return GrColorSpaceXform::Equals(this->fColorSpaceXform.get(), ge.fColorSpaceXform.get());
}
return false;
diff --git a/src/effects/gradients/SkGradientShaderPriv.h b/src/effects/gradients/SkGradientShaderPriv.h
index 2e35a47061..aeb1301c7f 100644
--- a/src/effects/gradients/SkGradientShaderPriv.h
+++ b/src/effects/gradients/SkGradientShaderPriv.h
@@ -12,6 +12,7 @@
#include "SkGradientShader.h"
#include "SkClampRange.h"
#include "SkColorPriv.h"
+#include "SkColorSpace.h"
#include "SkReadBuffer.h"
#include "SkWriteBuffer.h"
#include "SkMallocPixelRef.h"
@@ -84,6 +85,8 @@ public:
const SkMatrix* fLocalMatrix;
const SkColor* fColors;
+ const SkColor4f* fColors4f;
+ sk_sp<SkColorSpace> fColorSpace;
const SkScalar* fPos;
int fCount;
SkShader::TileMode fTileMode;
@@ -226,13 +229,16 @@ private:
enum {
kColorStorageCount = 4, // more than this many colors, and we'll use sk_malloc for the space
- kStorageSize = kColorStorageCount * (sizeof(SkColor) + sizeof(SkScalar) + sizeof(Rec))
+ kStorageSize = kColorStorageCount *
+ (sizeof(SkColor) + sizeof(SkScalar) + sizeof(Rec) + sizeof(SkColor4f))
};
- SkColor fStorage[(kStorageSize + 3) >> 2];
+ SkColor fStorage[(kStorageSize + 3) >> 2];
public:
- SkColor* fOrigColors; // original colors, before modulation by paint in context.
- SkScalar* fOrigPos; // original positions
- int fColorCount;
+ SkColor* fOrigColors; // original colors, before modulation by paint in context.
+ SkColor4f* fOrigColors4f; // original colors, as linear floats
+ SkScalar* fOrigPos; // original positions
+ int fColorCount;
+ sk_sp<SkColorSpace> fColorSpace; // color space of gradient stops
bool colorsAreOpaque() const { return fColorsAreOpaque; }
@@ -240,7 +246,7 @@ public:
Rec* getRecs() const { return fRecs; }
private:
- bool fColorsAreOpaque;
+ bool fColorsAreOpaque;
GradientShaderCache* refCache(U8CPU alpha, bool dither) const;
mutable SkMutex fCacheMutex;
@@ -265,6 +271,7 @@ static inline int next_dither_toggle(int toggle) {
#if SK_SUPPORT_GPU
+#include "GrColorSpaceXform.h"
#include "GrCoordTransform.h"
#include "GrFragmentProcessor.h"
#include "glsl/GrGLSLFragmentProcessor.h"
@@ -304,16 +311,22 @@ public:
CreateArgs(GrContext* context,
const SkGradientShaderBase* shader,
const SkMatrix* matrix,
- SkShader::TileMode tileMode)
+ SkShader::TileMode tileMode,
+ sk_sp<GrColorSpaceXform> colorSpaceXform,
+ bool gammaCorrect)
: fContext(context)
, fShader(shader)
, fMatrix(matrix)
- , fTileMode(tileMode) {}
+ , fTileMode(tileMode)
+ , fColorSpaceXform(std::move(colorSpaceXform))
+ , fGammaCorrect(gammaCorrect) {}
GrContext* fContext;
const SkGradientShaderBase* fShader;
const SkMatrix* fMatrix;
SkShader::TileMode fTileMode;
+ sk_sp<GrColorSpaceXform> fColorSpaceXform;
+ bool fGammaCorrect;
};
class GLSLProcessor;
@@ -361,6 +374,12 @@ public:
return &fColors[pos];
}
+ const SkColor4f* getColors4f(int pos) const {
+ SkASSERT(fColorType != kTexture_ColorType);
+ SkASSERT(pos < fColors4f.count());
+ return &fColors4f[pos];
+ }
+
protected:
/** Populates a pair of arrays with colors and stop info to construct a random gradient.
The function decides whether stop values should be used or not. The return value indicates
@@ -384,9 +403,15 @@ protected:
private:
static const GrCoordSet kCoordSet = kLocal_GrCoordSet;
- SkTDArray<SkColor> fColors;
- SkTDArray<SkScalar> fPositions;
- SkShader::TileMode fTileMode;
+ // If we're in legacy mode, then fColors will be populated. If we're gamma-correct, then
+ // fColors4f and fColorSpaceXform will be populated.
+ SkTDArray<SkColor> fColors;
+
+ SkTDArray<SkColor4f> fColors4f;
+ sk_sp<GrColorSpaceXform> fColorSpaceXform;
+
+ SkTDArray<SkScalar> fPositions;
+ SkShader::TileMode fTileMode;
GrCoordTransform fCoordTransform;
GrTextureAccess fTextureAccess;
diff --git a/src/effects/gradients/SkLinearGradient.cpp b/src/effects/gradients/SkLinearGradient.cpp
index a93235d31a..4bf5e3cc20 100644
--- a/src/effects/gradients/SkLinearGradient.cpp
+++ b/src/effects/gradients/SkLinearGradient.cpp
@@ -341,6 +341,7 @@ SkShader::GradientType SkLinearGradient::asAGradient(GradientInfo* info) const {
#if SK_SUPPORT_GPU
+#include "GrColorSpaceXform.h"
#include "glsl/GrGLSLCaps.h"
#include "glsl/GrGLSLFragmentShaderBuilder.h"
#include "SkGr.h"
@@ -461,8 +462,11 @@ sk_sp<GrFragmentProcessor> SkLinearGradient::asFragmentProcessor(const AsFPArgs&
}
matrix.postConcat(fPtsToUnit);
+ sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(),
+ args.fDstColorSpace);
sk_sp<GrFragmentProcessor> inner(GrLinearGradient::Make(
- GrGradientEffect::CreateArgs(args.fContext, this, &matrix, fTileMode)));
+ GrGradientEffect::CreateArgs(args.fContext, this, &matrix, fTileMode,
+ std::move(colorSpaceXform), SkToBool(args.fDstColorSpace))));
return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner));
}
diff --git a/src/effects/gradients/SkRadialGradient.cpp b/src/effects/gradients/SkRadialGradient.cpp
index 5691188db7..6eaecffedd 100644
--- a/src/effects/gradients/SkRadialGradient.cpp
+++ b/src/effects/gradients/SkRadialGradient.cpp
@@ -355,8 +355,11 @@ sk_sp<GrFragmentProcessor> SkRadialGradient::asFragmentProcessor(const AsFPArgs&
matrix.postConcat(inv);
}
matrix.postConcat(fPtsToUnit);
+ sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(),
+ args.fDstColorSpace);
sk_sp<GrFragmentProcessor> inner(GrRadialGradient::Make(
- GrGradientEffect::CreateArgs(args.fContext, this, &matrix, fTileMode)));
+ GrGradientEffect::CreateArgs(args.fContext, this, &matrix, fTileMode,
+ std::move(colorSpaceXform), SkToBool(args.fDstColorSpace))));
return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner));
}
diff --git a/src/effects/gradients/SkSweepGradient.cpp b/src/effects/gradients/SkSweepGradient.cpp
index 0789237048..58bb8aa8ed 100644
--- a/src/effects/gradients/SkSweepGradient.cpp
+++ b/src/effects/gradients/SkSweepGradient.cpp
@@ -247,8 +247,11 @@ sk_sp<GrFragmentProcessor> SkSweepGradient::asFragmentProcessor(const AsFPArgs&
}
matrix.postConcat(fPtsToUnit);
+ sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(),
+ args.fDstColorSpace);
sk_sp<GrFragmentProcessor> inner(GrSweepGradient::Make(
- GrGradientEffect::CreateArgs(args.fContext, this, &matrix, SkShader::kClamp_TileMode)));
+ GrGradientEffect::CreateArgs(args.fContext, this, &matrix, SkShader::kClamp_TileMode,
+ std::move(colorSpaceXform), SkToBool(args.fDstColorSpace))));
return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner));
}
diff --git a/src/effects/gradients/SkTwoPointConicalGradient.cpp b/src/effects/gradients/SkTwoPointConicalGradient.cpp
index 9558c1f3a1..8e3671b1f9 100644
--- a/src/effects/gradients/SkTwoPointConicalGradient.cpp
+++ b/src/effects/gradients/SkTwoPointConicalGradient.cpp
@@ -360,8 +360,11 @@ sk_sp<GrFragmentProcessor> SkTwoPointConicalGradient::asFragmentProcessor(
const AsFPArgs& args) const {
SkASSERT(args.fContext);
SkASSERT(fPtsToUnit.isIdentity());
+ sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(),
+ args.fDstColorSpace);
sk_sp<GrFragmentProcessor> inner(Gr2PtConicalGradientEffect::Make(
- GrGradientEffect::CreateArgs(args.fContext, this, args.fLocalMatrix, fTileMode)));
+ GrGradientEffect::CreateArgs(args.fContext, this, args.fLocalMatrix, fTileMode,
+ std::move(colorSpaceXform), SkToBool(args.fDstColorSpace))));
return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner));
}
diff --git a/src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp b/src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp
index b7cacc48ff..a9a7805d5b 100644
--- a/src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp
+++ b/src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp
@@ -1306,7 +1306,8 @@ sk_sp<GrFragmentProcessor> Gr2PtConicalGradientEffect::Make(
matrix.postConcat(inv);
}
- GrGradientEffect::CreateArgs newArgs(args.fContext, args.fShader, &matrix, args.fTileMode);
+ GrGradientEffect::CreateArgs newArgs(args.fContext, args.fShader, &matrix, args.fTileMode,
+ std::move(args.fColorSpaceXform), args.fGammaCorrect);
if (shader.getStartRadius() < kErrorTol) {
SkScalar focalX;
diff --git a/src/gpu/GrColorSpaceXform.cpp b/src/gpu/GrColorSpaceXform.cpp
index 3380b0f390..d4faaaffd1 100644
--- a/src/gpu/GrColorSpaceXform.cpp
+++ b/src/gpu/GrColorSpaceXform.cpp
@@ -59,6 +59,18 @@ sk_sp<GrColorSpaceXform> GrColorSpaceXform::Make(SkColorSpace* src, SkColorSpace
return sk_make_sp<GrColorSpaceXform>(srcToDst);
}
+bool GrColorSpaceXform::Equals(const GrColorSpaceXform* a, const GrColorSpaceXform* b) {
+ if (a == b) {
+ return true;
+ }
+
+ if (!a || !b) {
+ return false;
+ }
+
+ return a->fSrcToDst == b->fSrcToDst;
+}
+
GrColor4f GrColorSpaceXform::apply(const GrColor4f& srcColor) {
GrColor4f result;
fSrcToDst.mapScalars(srcColor.fRGBA, result.fRGBA);