diff --git a/src/shaders/gradients/SkGradientShader.cpp b/src/shaders/gradients/SkGradientShader.cpp index 279491b177..8b22430e91 100644 --- a/src/shaders/gradients/SkGradientShader.cpp +++ b/src/shaders/gradients/SkGradientShader.cpp @@ -8,6 +8,7 @@ #include #include "Sk4fLinearGradient.h" #include "SkColorSpace_XYZ.h" +#include "SkColorSpaceXformer.h" #include "SkFloatBits.h" #include "SkGradientBitmapCache.h" #include "SkGradientShaderPriv.h" @@ -117,6 +118,7 @@ bool SkGradientShaderBase::DescriptorScope::unflatten(SkReadBuffer& buffer) { SkGradientShaderBase::SkGradientShaderBase(const Descriptor& desc, const SkMatrix& ptsToUnit) : INHERITED(desc.fLocalMatrix) , fPtsToUnit(ptsToUnit) + , fColorsAreOpaque(true) { fPtsToUnit.getType(); // Precache so reads are threadsafe. SkASSERT(desc.fCount > 1); @@ -148,37 +150,30 @@ SkGradientShaderBase::SkGradientShaderBase(const Descriptor& desc, const SkMatri } if (fColorCount > kColorStorageCount) { - size_t size = sizeof(SkColor) + sizeof(SkColor4f); + size_t size = sizeof(SkColor4f); if (desc.fPos) { size += sizeof(SkScalar); } - fOrigColors = reinterpret_cast(sk_malloc_throw(size * fColorCount)); + fOrigColors4f = reinterpret_cast(sk_malloc_throw(size * fColorCount)); } else { - fOrigColors = fStorage; + fOrigColors4f = fStorage; } - fOrigColors4f = (SkColor4f*)(fOrigColors + fColorCount); - // Now copy over the colors, adding the dummies as needed SkColor4f* origColors = fOrigColors4f; if (dummyFirst) { *origColors++ = desc.fColors[0]; } - memcpy(origColors, desc.fColors, desc.fCount * sizeof(SkColor4f)); + for (int i = 0; i < desc.fCount; ++i) { + origColors[i] = desc.fColors[i]; + fColorsAreOpaque = fColorsAreOpaque && (desc.fColors[i].fA == 1); + } if (dummyLast) { origColors += desc.fCount; *origColors = desc.fColors[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 the common case (sRGB colors), this does the right thing. - for (int i = 0; i < fColorCount; ++i) { - fOrigColors[i] = fOrigColors4f[i].toSkColor(); - } - if (!desc.fColorSpace) { // This happens if we were constructed from SkColors, so our colors are really sRGB fColorSpace = SkColorSpace::MakeSRGBLinear(); @@ -221,23 +216,14 @@ SkGradientShaderBase::SkGradientShaderBase(const Descriptor& desc, const SkMatri fOrigPos = nullptr; } } - this->initCommon(); } SkGradientShaderBase::~SkGradientShaderBase() { - if (fOrigColors != fStorage) { - sk_free(fOrigColors); + if (fOrigColors4f != fStorage) { + sk_free(fOrigColors4f); } } -void SkGradientShaderBase::initCommon() { - unsigned colorAlpha = 0xFF; - for (int i = 0; i < fColorCount; i++) { - colorAlpha &= SkColorGetA(fOrigColors[i]); - } - fColorsAreOpaque = colorAlpha == 0xFF; -} - void SkGradientShaderBase::flatten(SkWriteBuffer& buffer) const { Descriptor desc; desc.fColors = fOrigColors4f; @@ -451,8 +437,9 @@ bool SkGradientShaderBase::onAsLuminanceColor(SkColor* lum) const { int g = 0; int b = 0; const int n = fColorCount; + // TODO: use linear colors? for (int i = 0; i < n; ++i) { - SkColor c = fOrigColors[i]; + SkColor c = this->getLegacyColor(i); r += SkColorGetR(c); g += SkColorGetG(c); b += SkColorGetB(c); @@ -461,6 +448,19 @@ bool SkGradientShaderBase::onAsLuminanceColor(SkColor* lum) const { return true; } +SkGradientShaderBase::AutoXformColors::AutoXformColors(const SkGradientShaderBase& grad, + SkColorSpaceXformer* xformer) + : fColors(grad.fColorCount) { + // TODO: stay in 4f to preserve precision? + + SkAutoSTMalloc<8, SkColor> origColors(grad.fColorCount); + for (int i = 0; i < grad.fColorCount; ++i) { + origColors[i] = grad.getLegacyColor(i); + } + + xformer->apply(fColors.get(), origColors.get(), grad.fColorCount); +} + static constexpr int kGradientTextureSize = 256; void SkGradientShaderBase::initLinearBitmap(SkBitmap* bitmap, GradientBitmapType bitmapType) const { @@ -532,7 +532,7 @@ void SkGradientShaderBase::initLinearBitmap(SkBitmap* bitmap, GradientBitmapType SkColor4f SkGradientShaderBase::getXformedColor(size_t i, SkColorSpace* dstCS) const { return dstCS ? to_colorspace(fOrigColors4f[i], fColorSpace.get(), dstCS) - : SkColor4f_from_SkColor(fOrigColors[i], nullptr); + : SkColor4f_from_SkColor(this->getLegacyColor(i), nullptr); } SK_DECLARE_STATIC_MUTEX(gGradientCacheMutex); @@ -546,17 +546,19 @@ SK_DECLARE_STATIC_MUTEX(gGradientCacheMutex); void SkGradientShaderBase::getGradientTableBitmap(SkBitmap* bitmap, GradientBitmapType bitmapType) const { // build our key: [numColors + colors[] + {positions[]} + flags + colorType ] - int count = 1 + fColorCount + 1 + 1; + static_assert(sizeof(SkColor4f) % sizeof(int32_t) == 0, ""); + const int colorsAsIntCount = fColorCount * sizeof(SkColor4f) / sizeof(int32_t); + int count = 1 + colorsAsIntCount + 1 + 1; if (fColorCount > 2) { count += fColorCount - 1; } - SkAutoSTMalloc<16, int32_t> storage(count); + SkAutoSTMalloc<64, int32_t> storage(count); int32_t* buffer = storage.get(); *buffer++ = fColorCount; - memcpy(buffer, fOrigColors, fColorCount * sizeof(SkColor)); - buffer += fColorCount; + memcpy(buffer, fOrigColors4f, fColorCount * sizeof(SkColor4f)); + buffer += colorsAsIntCount; if (fColorCount > 2) { for (int i = 1; i < fColorCount; i++) { *buffer++ = SkFloat2Bits(this->getPos(i)); @@ -608,7 +610,9 @@ void SkGradientShaderBase::commonAsAGradient(GradientInfo* info) const { if (info) { if (info->fColorCount >= fColorCount) { if (info->fColors) { - memcpy(info->fColors, fOrigColors, fColorCount * sizeof(SkColor)); + for (int i = 0; i < fColorCount; ++i) { + info->fColors[i] = this->getLegacyColor(i); + } } if (info->fColorOffsets) { for (int i = 0; i < fColorCount; ++i) { @@ -628,7 +632,7 @@ void SkGradientShaderBase::toString(SkString* str) const { str->appendf("%d colors: ", fColorCount); for (int i = 0; i < fColorCount; ++i) { - str->appendHex(fOrigColors[i], 8); + str->appendHex(this->getLegacyColor(i), 8); if (i < fColorCount-1) { str->append(", "); } @@ -1267,13 +1271,13 @@ GrGradientEffect::GrGradientEffect(ClassID classID, const CreateArgs& args, bool auto colorSpaceXform = GrColorSpaceXform::Make(shader.fColorSpace.get(), kRGBA_float_GrPixelConfig, args.fDstColorSpace); - SkASSERT(shader.fOrigColors && shader.fOrigColors4f); + SkASSERT(shader.fOrigColors4f); fColors4f.setCount(shader.fColorCount); for (int i = 0; i < shader.fColorCount; ++i) { if (args.fDstColorSpace) { fColors4f[i] = GrColor4f::FromSkColor4f(shader.fOrigColors4f[i]); } else { - GrColor grColor = SkColorToUnpremulGrColor(shader.fOrigColors[i]); + GrColor grColor = SkColorToUnpremulGrColor(shader.getLegacyColor(i)); fColors4f[i] = GrColor4f::FromGrColor(grColor); } diff --git a/src/shaders/gradients/SkGradientShaderPriv.h b/src/shaders/gradients/SkGradientShaderPriv.h index 0d8e7bef9a..7084dc67b8 100644 --- a/src/shaders/gradients/SkGradientShaderPriv.h +++ b/src/shaders/gradients/SkGradientShaderPriv.h @@ -15,8 +15,10 @@ #include "SkMatrix.h" #include "SkShaderBase.h" #include "SkTDArray.h" +#include "SkTemplates.h" class SkColorSpace; +class SkColorSpaceXformer; class SkRasterPipeline; class SkReadBuffer; class SkWriteBuffer; @@ -105,6 +107,12 @@ protected: return ctx; } + struct AutoXformColors { + AutoXformColors(const SkGradientShaderBase&, SkColorSpaceXformer*); + + SkAutoSTMalloc<8, SkColor> fColors; + }; + const SkMatrix fPtsToUnit; TileMode fTileMode; uint8_t fGradFlags; @@ -113,20 +121,25 @@ 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(SkColor4f)) + kStorageSize = kColorStorageCount * (sizeof(SkColor4f) + sizeof(SkScalar)) }; - SkColor fStorage[(kStorageSize + 3) >> 2]; + SkColor4f fStorage[(kStorageSize + sizeof(SkColor4f) - 1) / sizeof(SkColor4f)]; + public: SkScalar getPos(int i) const { SkASSERT(i < fColorCount); return fOrigPos ? fOrigPos[i] : SkIntToScalar(i) / (fColorCount - 1); } - SkColor* fOrigColors; // original colors, before modulation by paint in context. + SkColor getLegacyColor(int i) const { + SkASSERT(i < fColorCount); + return fOrigColors4f[i].toSkColor(); + } + SkColor4f* fOrigColors4f; // original colors, as linear floats SkScalar* fOrigPos; // original positions int fColorCount; - sk_sp fColorSpace; // color space of gradient stops + sk_sp fColorSpace; // color space of gradient stops bool colorsAreOpaque() const { return fColorsAreOpaque; } @@ -135,8 +148,6 @@ public: private: bool fColorsAreOpaque; - void initCommon(); - typedef SkShaderBase INHERITED; }; diff --git a/src/shaders/gradients/SkLinearGradient.cpp b/src/shaders/gradients/SkLinearGradient.cpp index 3b41e79710..4932679890 100644 --- a/src/shaders/gradients/SkLinearGradient.cpp +++ b/src/shaders/gradients/SkLinearGradient.cpp @@ -72,10 +72,9 @@ void SkLinearGradient::appendGradientStages(SkArenaAlloc*, SkRasterPipeline*, } sk_sp SkLinearGradient::onMakeColorSpace(SkColorSpaceXformer* xformer) const { + const AutoXformColors xformedColors(*this, xformer); SkPoint pts[2] = { fStart, fEnd }; - SkSTArray<8, SkColor> xformedColors(fColorCount); - xformer->apply(xformedColors.begin(), fOrigColors, fColorCount); - return SkGradientShader::MakeLinear(pts, xformedColors.begin(), fOrigPos, fColorCount, + return SkGradientShader::MakeLinear(pts, xformedColors.fColors.get(), fOrigPos, fColorCount, fTileMode, fGradFlags, &this->getLocalMatrix()); } diff --git a/src/shaders/gradients/SkRadialGradient.cpp b/src/shaders/gradients/SkRadialGradient.cpp index 5014eaea75..0dede71001 100644 --- a/src/shaders/gradients/SkRadialGradient.cpp +++ b/src/shaders/gradients/SkRadialGradient.cpp @@ -196,9 +196,8 @@ std::unique_ptr SkRadialGradient::asFragmentProcessor( #endif sk_sp SkRadialGradient::onMakeColorSpace(SkColorSpaceXformer* xformer) const { - SkSTArray<8, SkColor> xformedColors(fColorCount); - xformer->apply(xformedColors.begin(), fOrigColors, fColorCount); - return SkGradientShader::MakeRadial(fCenter, fRadius, xformedColors.begin(), fOrigPos, + const AutoXformColors xformedColors(*this, xformer); + return SkGradientShader::MakeRadial(fCenter, fRadius, xformedColors.fColors.get(), fOrigPos, fColorCount, fTileMode, fGradFlags, &this->getLocalMatrix()); } diff --git a/src/shaders/gradients/SkSweepGradient.cpp b/src/shaders/gradients/SkSweepGradient.cpp index 75622217b4..1f24680476 100644 --- a/src/shaders/gradients/SkSweepGradient.cpp +++ b/src/shaders/gradients/SkSweepGradient.cpp @@ -251,14 +251,13 @@ std::unique_ptr SkSweepGradient::asFragmentProcessor( #endif sk_sp SkSweepGradient::onMakeColorSpace(SkColorSpaceXformer* xformer) const { - SkSTArray<8, SkColor> xformedColors(fColorCount); - xformer->apply(xformedColors.begin(), fOrigColors, fColorCount); + const AutoXformColors xformedColors(*this, xformer); SkScalar startAngle, endAngle; std::tie(startAngle, endAngle) = angles_from_t_coeff(fTBias, fTScale); - return SkGradientShader::MakeSweep(fCenter.fX, fCenter.fY, xformedColors.begin(), fOrigPos, - fColorCount, fTileMode, startAngle, endAngle, + return SkGradientShader::MakeSweep(fCenter.fX, fCenter.fY, xformedColors.fColors.get(), + fOrigPos, fColorCount, fTileMode, startAngle, endAngle, fGradFlags, &this->getLocalMatrix()); } diff --git a/src/shaders/gradients/SkTwoPointConicalGradient.cpp b/src/shaders/gradients/SkTwoPointConicalGradient.cpp index 86625de41e..cd7bbb1252 100644 --- a/src/shaders/gradients/SkTwoPointConicalGradient.cpp +++ b/src/shaders/gradients/SkTwoPointConicalGradient.cpp @@ -140,10 +140,9 @@ std::unique_ptr SkTwoPointConicalGradient::asFragmentProces #endif sk_sp SkTwoPointConicalGradient::onMakeColorSpace(SkColorSpaceXformer* xformer) const { - SkSTArray<8, SkColor> xformedColors(fColorCount); - xformer->apply(xformedColors.begin(), fOrigColors, fColorCount); + const AutoXformColors xformedColors(*this, xformer); return SkGradientShader::MakeTwoPointConical(fCenter1, fRadius1, fCenter2, fRadius2, - xformedColors.begin(), fOrigPos, fColorCount, + xformedColors.fColors.get(), fOrigPos, fColorCount, fTileMode, fGradFlags, &this->getLocalMatrix()); }