diff options
-rw-r--r-- | include/gpu/GrColorSpaceXform.h | 4 | ||||
-rw-r--r-- | src/effects/gradients/SkGradientShader.cpp | 133 | ||||
-rw-r--r-- | src/effects/gradients/SkGradientShaderPriv.h | 47 | ||||
-rw-r--r-- | src/effects/gradients/SkLinearGradient.cpp | 6 | ||||
-rw-r--r-- | src/effects/gradients/SkRadialGradient.cpp | 5 | ||||
-rw-r--r-- | src/effects/gradients/SkSweepGradient.cpp | 5 | ||||
-rw-r--r-- | src/effects/gradients/SkTwoPointConicalGradient.cpp | 5 | ||||
-rw-r--r-- | src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp | 3 | ||||
-rw-r--r-- | src/gpu/GrColorSpaceXform.cpp | 12 |
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); |