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);
}
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;
}
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);
}
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];
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) {
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;
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;
}
}
+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) {
#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;
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);
}
}
} 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;
}
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;
#include "SkGradientShader.h"
#include "SkClampRange.h"
#include "SkColorPriv.h"
+#include "SkColorSpace.h"
#include "SkReadBuffer.h"
#include "SkWriteBuffer.h"
#include "SkMallocPixelRef.h"
const SkMatrix* fLocalMatrix;
const SkColor* fColors;
+ const SkColor4f* fColors4f;
+ sk_sp<SkColorSpace> fColorSpace;
const SkScalar* fPos;
int fCount;
SkShader::TileMode fTileMode;
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; }
Rec* getRecs() const { return fRecs; }
private:
- bool fColorsAreOpaque;
+ bool fColorsAreOpaque;
GradientShaderCache* refCache(U8CPU alpha, bool dither) const;
mutable SkMutex fCacheMutex;
#if SK_SUPPORT_GPU
+#include "GrColorSpaceXform.h"
#include "GrCoordTransform.h"
#include "GrFragmentProcessor.h"
#include "glsl/GrGLSLFragmentProcessor.h"
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;
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
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;