aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar Brian Osman <brianosman@google.com>2018-06-18 10:20:32 -0400
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2018-06-18 14:55:48 +0000
commit3567c14a41cd55860fcc836af32d8748c1e3c856 (patch)
tree8a9428dd0c25e3dcee1ccf2e6008c1f665dcaa26 /src
parent1bb47df4fc8edf62b0463d088214ed1ffb909ca9 (diff)
Implement nonlinear (as-encoded) blending in GrColorSpaceXform
Make GrColorSpaceXform a wrapper over SkColorSpaceXformSteps, and removed the xform cache. The shader code does up to five steps to correctly transform (unpremul, linearize, gamut, encode, premul). Remove all clamping, so we can support sRGB encoded F16. Most of https://skia-review.googlesource.com/c/skia/+/132090, except that GrNonlinearColorSpaceXformEffect is still used for SkColorSpaceXformCanvas and a few other places. As a result, this doesn't trigger any layout test failures. Change-Id: I789a5e327a419b5f7634c00d1b355912046c07b7 Reviewed-on: https://skia-review.googlesource.com/135326 Reviewed-by: Brian Salomon <bsalomon@google.com> Reviewed-by: Mike Klein <mtklein@google.com> Commit-Queue: Brian Osman <brianosman@google.com>
Diffstat (limited to 'src')
-rw-r--r--src/gpu/GrColorSpaceInfo.cpp3
-rw-r--r--src/gpu/GrColorSpaceXform.cpp231
-rw-r--r--src/gpu/GrColorSpaceXform.h51
-rw-r--r--src/gpu/SkGr.cpp2
-rw-r--r--src/gpu/effects/GrNonlinearColorSpaceXformEffect.cpp8
-rw-r--r--src/gpu/glsl/GrGLSLColorSpaceXformHelper.h56
-rw-r--r--src/gpu/glsl/GrGLSLShaderBuilder.cpp66
-rw-r--r--src/shaders/SkColorShader.cpp8
8 files changed, 171 insertions, 254 deletions
diff --git a/src/gpu/GrColorSpaceInfo.cpp b/src/gpu/GrColorSpaceInfo.cpp
index 73218b26c5..60c848b8cf 100644
--- a/src/gpu/GrColorSpaceInfo.cpp
+++ b/src/gpu/GrColorSpaceInfo.cpp
@@ -17,7 +17,8 @@ GrColorSpaceXform* GrColorSpaceInfo::colorSpaceXformFromSRGB() const {
if (!fInitializedColorSpaceXformFromSRGB) {
// sRGB sources are very common (SkColor, etc...), so we cache that transformation
auto srgbColorSpace = SkColorSpace::MakeSRGB();
- fColorXformFromSRGB = GrColorSpaceXform::Make(srgbColorSpace.get(), fColorSpace.get());
+ fColorXformFromSRGB = GrColorSpaceXform::MakeUnpremulToUnpremul(srgbColorSpace.get(),
+ fColorSpace.get());
fInitializedColorSpaceXformFromSRGB = true;
}
// You can't be color-space aware in legacy mode
diff --git a/src/gpu/GrColorSpaceXform.cpp b/src/gpu/GrColorSpaceXform.cpp
index baa17df7bd..4ccca52185 100644
--- a/src/gpu/GrColorSpaceXform.cpp
+++ b/src/gpu/GrColorSpaceXform.cpp
@@ -9,135 +9,44 @@
#include "SkColorSpace.h"
#include "SkColorSpacePriv.h"
#include "SkMatrix44.h"
-#include "SkSpinlock.h"
#include "glsl/GrGLSLColorSpaceXformHelper.h"
#include "glsl/GrGLSLFragmentProcessor.h"
#include "glsl/GrGLSLFragmentShaderBuilder.h"
-class GrColorSpaceXformCache {
-public:
- using NewValueFn = std::function<sk_sp<GrColorSpaceXform>(void)>;
-
- GrColorSpaceXformCache() : fSequence(0) {}
-
- sk_sp<GrColorSpaceXform> findOrAdd(uint64_t key, NewValueFn newValue) {
- int oldest = 0;
- for (int i = 0; i < kEntryCount; ++i) {
- if (fEntries[i].fKey == key) {
- fEntries[i].fLastUse = fSequence++;
- return fEntries[i].fXform;
- }
- if (fEntries[i].fLastUse < fEntries[oldest].fLastUse) {
- oldest = i;
- }
- }
- fEntries[oldest].fKey = key;
- fEntries[oldest].fXform = newValue();
- fEntries[oldest].fLastUse = fSequence++;
- return fEntries[oldest].fXform;
- }
-
-private:
- enum { kEntryCount = 32 };
-
- struct Entry {
- // The default Entry is "valid". Any 64-bit key that is the same 32-bit value repeated
- // implies no xform is necessary, so nullptr should be returned. This particular case should
- // never happen, but by initializing all entries with this data, we can avoid special cases
- // for the array not yet being full.
- Entry() : fKey(0), fXform(nullptr), fLastUse(0) {}
-
- uint64_t fKey;
- sk_sp<GrColorSpaceXform> fXform;
- uint64_t fLastUse;
- };
-
- Entry fEntries[kEntryCount];
- uint64_t fSequence;
-};
-
-GrColorSpaceXform::GrColorSpaceXform(const SkColorSpaceTransferFn& srcTransferFn,
- const SkMatrix44& gamutXform, uint32_t flags)
- : fSrcTransferFn(srcTransferFn), fGamutXform(gamutXform), fFlags(flags) {}
-
-static SkSpinlock gColorSpaceXformCacheSpinlock;
-
sk_sp<GrColorSpaceXform> GrColorSpaceXform::Make(SkColorSpace* src, SkColorSpace* dst) {
+ // No transformation is performed in legacy mode, until SkColorSpaceXformCanvas is gone
if (!dst) {
- // No transformation is performed in legacy mode
return nullptr;
}
- // Treat null sources as sRGB
+ // Treat null sources as sRGB (safe because sRGB is a global singleton)
if (!src) {
src = SkColorSpace::MakeSRGB().get();
}
- uint32_t flags = 0;
- SkColorSpaceTransferFn srcTransferFn;
-
- if (src->gammaIsLinear()) {
- // Linear sampling does the right thing
- } else if (src->isNumericalTransferFn(&srcTransferFn)) {
- // Need to manually apply some transfer function
- flags |= kApplyTransferFn_Flag;
- } else {
- // We don't (yet) support more complex transfer functions
- return nullptr;
- }
+ // TODO: Plumb source alpha type
+ SkColorSpaceXformSteps steps(src, kPremul_SkAlphaType, dst);
- if (src == dst && (0 == flags)) {
- // Quick equality check - no conversion (or transfer function) needed in this case
- return nullptr;
- }
+ return steps.flags.mask() == 0 ? nullptr /* Noop transform */
+ : sk_make_sp<GrColorSpaceXform>(steps);
+}
- const SkMatrix44* toXYZD50 = src->toXYZD50();
- const SkMatrix44* fromXYZD50 = dst->fromXYZD50();
- if (!toXYZD50 || !fromXYZD50) {
- // Unsupported colour spaces -- cannot specify gamut as a matrix
+sk_sp<GrColorSpaceXform> GrColorSpaceXform::MakeUnpremulToUnpremul(SkColorSpace* src,
+ SkColorSpace* dst) {
+ // No transformation is performed in legacy mode, until SkColorSpaceXformCanvas is gone
+ if (!dst) {
return nullptr;
}
- // Determine if a gamut xform is needed
- uint32_t srcHash = src->toXYZD50Hash();
- uint32_t dstHash = dst->toXYZD50Hash();
- if (srcHash != dstHash) {
- flags |= kApplyGamutXform_Flag;
- } else {
- SkASSERT(*toXYZD50 == *dst->toXYZD50() && "Hash collision");
- }
-
- if (0 == flags) {
- // Identical gamut and no transfer function - no conversion needed in this case
- return nullptr;
+ // Treat null sources as sRGB (safe because sRGB is a global singleton)
+ if (!src) {
+ src = SkColorSpace::MakeSRGB().get();
}
- auto makeXform = [srcTransferFn, fromXYZD50, toXYZD50, flags]() {
- SkMatrix44 srcToDst(SkMatrix44::kUninitialized_Constructor);
- if (SkToBool(flags & kApplyGamutXform_Flag)) {
- srcToDst.setConcat(*fromXYZD50, *toXYZD50);
- } else {
- srcToDst.setIdentity();
- }
- return sk_make_sp<GrColorSpaceXform>(srcTransferFn, srcToDst, flags);
- };
-
- // For now, we only cache pure gamut xforms (no transfer functions)
- // TODO: Fold a hash of the transfer function into the cache key
- if ((kApplyGamutXform_Flag == flags) && gColorSpaceXformCacheSpinlock.tryAcquire()) {
- static GrColorSpaceXformCache* gCache;
- if (nullptr == gCache) {
- gCache = new GrColorSpaceXformCache();
- }
+ SkColorSpaceXformSteps steps = SkColorSpaceXformSteps::UnpremulToUnpremul(src, dst);
- uint64_t key = static_cast<uint64_t>(srcHash) << 32 | static_cast<uint64_t>(dstHash);
- sk_sp<GrColorSpaceXform> xform = gCache->findOrAdd(key, makeXform);
- gColorSpaceXformCacheSpinlock.release();
- return xform;
- } else {
- // If our xform has non-gamut components, or we can't get the spin lock, just build it
- return makeXform();
- }
+ return steps.flags.mask() == 0 ? nullptr /* Noop transform */
+ : sk_make_sp<GrColorSpaceXform>(steps);
}
bool GrColorSpaceXform::Equals(const GrColorSpaceXform* a, const GrColorSpaceXform* b) {
@@ -145,45 +54,32 @@ bool GrColorSpaceXform::Equals(const GrColorSpaceXform* a, const GrColorSpaceXfo
return true;
}
- if (!a || !b || a->fFlags != b->fFlags) {
+ if (!a || !b || a->fSteps.flags.mask() != b->fSteps.flags.mask()) {
+ return false;
+ }
+
+ if (a->fSteps.flags.linearize &&
+ 0 != memcmp(&a->fSteps.srcTF, &b->fSteps.srcTF, sizeof(a->fSteps.srcTF))) {
return false;
}
- if (SkToBool(a->fFlags & kApplyTransferFn_Flag) &&
- 0 != memcmp(&a->fSrcTransferFn, &b->fSrcTransferFn, sizeof(SkColorSpaceTransferFn))) {
+ if (a->fSteps.flags.gamut_transform &&
+ 0 != memcmp(&a->fSteps.src_to_dst_matrix, &b->fSteps.src_to_dst_matrix,
+ sizeof(a->fSteps.src_to_dst_matrix))) {
return false;
}
- if (SkToBool(a->fFlags & kApplyGamutXform_Flag) && a->fGamutXform != b->fGamutXform) {
+ if (a->fSteps.flags.encode &&
+ 0 != memcmp(&a->fSteps.dstTFInv, &b->fSteps.dstTFInv, sizeof(a->fSteps.dstTFInv))) {
return false;
}
return true;
}
-GrColor4f GrColorSpaceXform::unclampedXform(const GrColor4f& srcColor) {
- // This transform step should only happen with textures (not CPU xform of individual values)
- SkASSERT(!SkToBool(fFlags & kApplyInverseSRGB_Flag));
-
+GrColor4f GrColorSpaceXform::apply(const GrColor4f& srcColor) {
GrColor4f result = srcColor;
- if (fFlags & kApplyTransferFn_Flag) {
- // Only transform RGB (not alpha)
- for (int i = 0; i < 3; ++i) {
- result.fRGBA[i] = fSrcTransferFn(result.fRGBA[i]);
- }
- }
- if (fFlags & kApplyGamutXform_Flag) {
- fGamutXform.mapScalars(result.fRGBA, result.fRGBA);
- }
- return result;
-}
-
-GrColor4f GrColorSpaceXform::clampedXform(const GrColor4f& srcColor) {
- GrColor4f result = this->unclampedXform(srcColor);
- for (int i = 0; i < 4; ++i) {
- // We always operate on unpremul colors, so clamp to [0,1].
- result.fRGBA[i] = SkTPin(result.fRGBA[i], 0.0f, 1.0f);
- }
+ fSteps.apply(result.fRGBA);
return result;
}
@@ -198,13 +94,22 @@ public:
fColorSpaceHelper.emitCode(uniformHandler, csxe.colorXform());
- SkString childColor("src_color");
- this->emitChild(0, &childColor, args);
+ if (this->numChildProcessors()) {
+ SkString childColor("src_color");
+ this->emitChild(0, &childColor, args);
- SkString xformedColor;
- fragBuilder->appendColorGamutXform(&xformedColor, childColor.c_str(), &fColorSpaceHelper);
- fragBuilder->codeAppendf("%s = %s * %s;", args.fOutputColor, xformedColor.c_str(),
- args.fInputColor);
+ SkString xformedColor;
+ fragBuilder->appendColorGamutXform(&xformedColor, childColor.c_str(), &fColorSpaceHelper);
+ fragBuilder->codeAppendf("%s = %s * %s;", args.fOutputColor, xformedColor.c_str(),
+ args.fInputColor);
+ } else {
+ if (nullptr == args.fInputColor) {
+ args.fInputColor = "half4(1)";
+ }
+ SkString xformedColor;
+ fragBuilder->appendColorGamutXform(&xformedColor, args.fInputColor, &fColorSpaceHelper);
+ fragBuilder->codeAppendf("%s = %s;", args.fOutputColor, xformedColor.c_str());
+ }
}
private:
@@ -225,12 +130,16 @@ GrColorSpaceXformEffect::GrColorSpaceXformEffect(std::unique_ptr<GrFragmentProce
sk_sp<GrColorSpaceXform> colorXform)
: INHERITED(kGrColorSpaceXformEffect_ClassID, OptFlags(child.get()))
, fColorXform(std::move(colorXform)) {
- this->registerChildProcessor(std::move(child));
+ if (child) {
+ this->registerChildProcessor(std::move(child));
+ }
}
std::unique_ptr<GrFragmentProcessor> GrColorSpaceXformEffect::clone() const {
+ std::unique_ptr<GrFragmentProcessor> child =
+ this->numChildProcessors() ? this->childProcessor(0).clone() : nullptr;
return std::unique_ptr<GrFragmentProcessor>(
- new GrColorSpaceXformEffect(this->childProcessor(0).clone(), fColorXform));
+ new GrColorSpaceXformEffect(std::move(child), fColorXform));
}
bool GrColorSpaceXformEffect::onIsEqual(const GrFragmentProcessor& s) const {
@@ -250,14 +159,30 @@ GrGLSLFragmentProcessor* GrColorSpaceXformEffect::onCreateGLSLInstance() const {
GrFragmentProcessor::OptimizationFlags GrColorSpaceXformEffect::OptFlags(
const GrFragmentProcessor* child) {
// TODO: Implement constant output for constant input
- OptimizationFlags flags = kNone_OptimizationFlags;
- if (child->compatibleWithCoverageAsAlpha()) {
- flags |= kCompatibleWithCoverageAsAlpha_OptimizationFlag;
+ if (child) {
+ OptimizationFlags flags = kNone_OptimizationFlags;
+ if (child->compatibleWithCoverageAsAlpha()) {
+ flags |= kCompatibleWithCoverageAsAlpha_OptimizationFlag;
+ }
+ if (child->preservesOpaqueInput()) {
+ flags |= kPreservesOpaqueInput_OptimizationFlag;
+ }
+ return flags;
+ } else {
+ return kCompatibleWithCoverageAsAlpha_OptimizationFlag |
+ kPreservesOpaqueInput_OptimizationFlag;
}
- if (child->preservesOpaqueInput()) {
- flags |= kPreservesOpaqueInput_OptimizationFlag;
+}
+
+std::unique_ptr<GrFragmentProcessor> GrColorSpaceXformEffect::Make(SkColorSpace* src,
+ SkColorSpace* dst) {
+ auto xform = GrColorSpaceXform::Make(src, dst);
+ if (!xform) {
+ return nullptr;
}
- return flags;
+
+ return std::unique_ptr<GrFragmentProcessor>(new GrColorSpaceXformEffect(nullptr,
+ std::move(xform)));
}
std::unique_ptr<GrFragmentProcessor> GrColorSpaceXformEffect::Make(
@@ -267,11 +192,11 @@ std::unique_ptr<GrFragmentProcessor> GrColorSpaceXformEffect::Make(
return nullptr;
}
- auto colorXform = GrColorSpaceXform::Make(src, dst);
- if (colorXform) {
- return std::unique_ptr<GrFragmentProcessor>(
- new GrColorSpaceXformEffect(std::move(child), std::move(colorXform)));
- } else {
+ auto xform = GrColorSpaceXform::Make(src, dst);
+ if (!xform) {
return child;
}
+
+ return std::unique_ptr<GrFragmentProcessor>(new GrColorSpaceXformEffect(std::move(child),
+ std::move(xform)));
}
diff --git a/src/gpu/GrColorSpaceXform.h b/src/gpu/GrColorSpaceXform.h
index b925fb2b87..2dca9c75cf 100644
--- a/src/gpu/GrColorSpaceXform.h
+++ b/src/gpu/GrColorSpaceXform.h
@@ -10,72 +10,51 @@
#include "GrColor.h"
#include "GrFragmentProcessor.h"
-#include "SkColorSpace.h"
-#include "SkMatrix44.h"
+#include "SkColorSpaceXformSteps.h"
#include "SkRefCnt.h"
+class SkColorSpace;
+
/**
* Represents a color space transformation
*/
class GrColorSpaceXform : public SkRefCnt {
public:
- GrColorSpaceXform(const SkColorSpaceTransferFn&, const SkMatrix44&, uint32_t);
+ GrColorSpaceXform(const SkColorSpaceXformSteps& steps) : fSteps(steps) {}
static sk_sp<GrColorSpaceXform> Make(SkColorSpace* src, SkColorSpace* dst);
- static sk_sp<GrColorSpaceXform> MakeGamutXform(SkColorSpace* src, SkColorSpace* dst) {
- sk_sp<SkColorSpace> linearSrc = sk_ref_sp(src);
- if (!linearSrc) {
- linearSrc = SkColorSpace::MakeSRGBLinear();
- }
- linearSrc = linearSrc->makeLinearGamma();
- auto result = Make(linearSrc.get(), dst);
- SkASSERT(!result || 0 == (result->fFlags & ~kApplyGamutXform_Flag));
- return result;
- }
- const SkColorSpaceTransferFn& transferFn() const { return fSrcTransferFn; }
- const float* transferFnCoeffs() const {
- static_assert(0 == offsetof(SkColorSpaceTransferFn, fG), "TransferFn layout");
- return &fSrcTransferFn.fG;
- }
+ static sk_sp<GrColorSpaceXform> MakeUnpremulToUnpremul(SkColorSpace* src, SkColorSpace* dst);
- const SkMatrix44& gamutXform() const { return fGamutXform; }
+ const SkColorSpaceXformSteps& steps() const { return fSteps; }
/**
* GrGLSLFragmentProcessor::GenKey() must call this and include the returned value in its
* computed key.
*/
static uint32_t XformKey(const GrColorSpaceXform* xform) {
- // Code generation depends on which steps we apply (as encoded by fFlags)
- return SkToBool(xform) ? xform->fFlags : 0;
+ // Code generation depends on which steps we apply
+ return xform ? xform->fSteps.flags.mask() : 0;
}
static bool Equals(const GrColorSpaceXform* a, const GrColorSpaceXform* b);
- GrColor4f unclampedXform(const GrColor4f& srcColor);
- GrColor4f clampedXform(const GrColor4f& srcColor);
+ GrColor4f apply(const GrColor4f& srcColor);
private:
friend class GrGLSLColorSpaceXformHelper;
- enum Flags {
- kApplyTransferFn_Flag = 0x1,
- kApplyGamutXform_Flag = 0x2,
-
- // Almost never used. This handles the case where the src data is sRGB pixel config,
- // but the color space has a different transfer function. In that case, we first undo
- // the HW sRGB -> Linear conversion, before applying any other steps.
- kApplyInverseSRGB_Flag = 0x4,
- };
-
- SkColorSpaceTransferFn fSrcTransferFn;
- SkMatrix44 fGamutXform;
- uint32_t fFlags;
+ SkColorSpaceXformSteps fSteps;
};
class GrColorSpaceXformEffect : public GrFragmentProcessor {
public:
/**
+ * Returns a fragment processor that converts the input's color space from src to dst.
+ */
+ static std::unique_ptr<GrFragmentProcessor> Make(SkColorSpace* src, SkColorSpace* dst);
+
+ /**
* Returns a fragment processor that calls the passed in fragment processor, and then converts
* the color space of the output from src to dst.
*/
diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp
index 9a13398c33..f872756159 100644
--- a/src/gpu/SkGr.cpp
+++ b/src/gpu/SkGr.cpp
@@ -221,7 +221,7 @@ GrColor4f SkColorToPremulGrColor4fLegacy(SkColor c) {
GrColor4f SkColorToUnpremulGrColor4f(SkColor c, const GrColorSpaceInfo& colorSpaceInfo) {
GrColor4f color = GrColor4f::FromGrColor(SkColorToUnpremulGrColor(c));
if (auto* xform = colorSpaceInfo.colorSpaceXformFromSRGB()) {
- color = xform->clampedXform(color);
+ color = xform->apply(color);
}
return color;
}
diff --git a/src/gpu/effects/GrNonlinearColorSpaceXformEffect.cpp b/src/gpu/effects/GrNonlinearColorSpaceXformEffect.cpp
index 5471f563fb..cec9da3590 100644
--- a/src/gpu/effects/GrNonlinearColorSpaceXformEffect.cpp
+++ b/src/gpu/effects/GrNonlinearColorSpaceXformEffect.cpp
@@ -227,12 +227,12 @@ std::unique_ptr<GrFragmentProcessor> GrNonlinearColorSpaceXformEffect::Make(SkCo
uint32_t ops = 0;
- // We rely on GrColorSpaceXform to build the gamut xform matrix for us (to get caching)
- auto gamutXform = GrColorSpaceXform::MakeGamutXform(src, dst);
+ const SkMatrix44* toXYZ = src->toXYZD50();
+ const SkMatrix44* fromXYZ = dst->fromXYZD50();
SkMatrix44 srcToDstMtx(SkMatrix44::kUninitialized_Constructor);
- if (gamutXform) {
+ if (toXYZ && fromXYZ && src->toXYZD50Hash() != dst->toXYZD50Hash()) {
+ srcToDstMtx.setConcat(*fromXYZ, *toXYZ);
ops |= kGamutXform_Op;
- srcToDstMtx = gamutXform->gamutXform();
}
SkColorSpaceTransferFn srcTransferFn;
diff --git a/src/gpu/glsl/GrGLSLColorSpaceXformHelper.h b/src/gpu/glsl/GrGLSLColorSpaceXformHelper.h
index 0cc9da1012..397a656c88 100644
--- a/src/gpu/glsl/GrGLSLColorSpaceXformHelper.h
+++ b/src/gpu/glsl/GrGLSLColorSpaceXformHelper.h
@@ -10,6 +10,7 @@
#include "GrColorSpaceXform.h"
#include "GrGLSLUniformHandler.h"
+#include "SkColorSpaceXformSteps.h"
/**
* Helper class to assist with using GrColorSpaceXform within an FP. This manages all of the
@@ -18,56 +19,61 @@
*/
class GrGLSLColorSpaceXformHelper : public SkNoncopyable {
public:
- GrGLSLColorSpaceXformHelper() : fFlags(0) {}
+ GrGLSLColorSpaceXformHelper() {
+ memset(&fFlags, 0, sizeof(fFlags));
+ }
void emitCode(GrGLSLUniformHandler* uniformHandler, const GrColorSpaceXform* colorSpaceXform,
uint32_t visibility = kFragment_GrShaderFlag) {
SkASSERT(uniformHandler);
if (colorSpaceXform) {
- fFlags = colorSpaceXform->fFlags;
+ fFlags = colorSpaceXform->fSteps.flags;
+ if (this->applySrcTF()) {
+ fSrcTFVar = uniformHandler->addUniformArray(visibility, kHalf_GrSLType,
+ "SrcTF", kNumTransferFnCoeffs);
+ }
if (this->applyGamutXform()) {
- fGamutXformVar = uniformHandler->addUniform(visibility,
- kHalf4x4_GrSLType,
+ fGamutXformVar = uniformHandler->addUniform(visibility, kHalf3x3_GrSLType,
"ColorXform");
}
- if (this->applyTransferFn()) {
- fTransferFnVar = uniformHandler->addUniformArray(visibility,
- kHalf_GrSLType,
- "TransferFn",
- kNumTransferFnCoeffs);
+ if (this->applyDstTF()) {
+ fDstTFVar = uniformHandler->addUniformArray(visibility, kHalf_GrSLType,
+ "DstTF", kNumTransferFnCoeffs);
}
}
}
void setData(const GrGLSLProgramDataManager& pdman, const GrColorSpaceXform* colorSpaceXform) {
+ if (this->applySrcTF()) {
+ pdman.set1fv(fSrcTFVar, kNumTransferFnCoeffs, &colorSpaceXform->fSteps.srcTF.fG);
+ }
if (this->applyGamutXform()) {
- pdman.setSkMatrix44(fGamutXformVar, colorSpaceXform->gamutXform());
+ pdman.setMatrix3f(fGamutXformVar, colorSpaceXform->fSteps.src_to_dst_matrix);
}
- if (this->applyTransferFn()) {
- pdman.set1fv(fTransferFnVar, kNumTransferFnCoeffs, colorSpaceXform->transferFnCoeffs());
+ if (this->applyDstTF()) {
+ pdman.set1fv(fDstTFVar, kNumTransferFnCoeffs, &colorSpaceXform->fSteps.dstTFInv.fG);
}
}
- bool isNoop() const { return (0 == fFlags); }
- bool applyInverseSRGB() const {
- return SkToBool(fFlags & GrColorSpaceXform::kApplyInverseSRGB_Flag);
- }
- bool applyTransferFn() const {
- return SkToBool(fFlags & GrColorSpaceXform::kApplyTransferFn_Flag);
- }
- bool applyGamutXform() const {
- return SkToBool(fFlags & GrColorSpaceXform::kApplyGamutXform_Flag);
- }
+ bool isNoop() const { return (0 == fFlags.mask()); }
+
+ bool applyUnpremul() const { return fFlags.unpremul; }
+ bool applySrcTF() const { return fFlags.linearize; }
+ bool applyGamutXform() const { return fFlags.gamut_transform; }
+ bool applyDstTF() const { return fFlags.encode; }
+ bool applyPremul() const { return fFlags.premul; }
+ GrGLSLProgramDataManager::UniformHandle srcTFUniform() const { return fSrcTFVar; }
GrGLSLProgramDataManager::UniformHandle gamutXformUniform() const { return fGamutXformVar; }
- GrGLSLProgramDataManager::UniformHandle transferFnUniform() const { return fTransferFnVar; }
+ GrGLSLProgramDataManager::UniformHandle dstTFUniform() const { return fDstTFVar; }
private:
static const int kNumTransferFnCoeffs = 7;
+ GrGLSLProgramDataManager::UniformHandle fSrcTFVar;
GrGLSLProgramDataManager::UniformHandle fGamutXformVar;
- GrGLSLProgramDataManager::UniformHandle fTransferFnVar;
- uint32_t fFlags;
+ GrGLSLProgramDataManager::UniformHandle fDstTFVar;
+ SkColorSpaceXformSteps::Flags fFlags;
};
#endif
diff --git a/src/gpu/glsl/GrGLSLShaderBuilder.cpp b/src/gpu/glsl/GrGLSLShaderBuilder.cpp
index db0be21f01..e2b1c559bc 100644
--- a/src/gpu/glsl/GrGLSLShaderBuilder.cpp
+++ b/src/gpu/glsl/GrGLSLShaderBuilder.cpp
@@ -109,24 +109,13 @@ void GrGLSLShaderBuilder::appendColorGamutXform(SkString* out,
GrGLSLUniformHandler* uniformHandler = fProgramBuilder->uniformHandler();
- // We define up to three helper functions, to keep things clearer. One does inverse sRGB,
- // one does an arbitrary transfer function, and the last does gamut xform. Any combination of
- // these may be present, although some configurations are much more likely.
+ // We define up to three helper functions, to keep things clearer. One for the source transfer
+ // function, one for the (inverse) destination transfer function, and one for the gamut xform.
+ // Any combination of these may be present, although some configurations are much more likely.
- SkString inverseSrgbFuncName;
- if (colorXformHelper->applyInverseSRGB()) {
- static const GrShaderVar gInverseSRGBArgs[] = { GrShaderVar("x", kHalf_GrSLType) };
- SkString body;
- body.append("return (x <= 0.0031308) ? (x * 12.92) : (1.055 * pow(x, 0.4166667) - 0.055);");
- this->emitFunction(kHalf_GrSLType, "inverse_srgb", SK_ARRAY_COUNT(gInverseSRGBArgs),
- gInverseSRGBArgs, body.c_str(), &inverseSrgbFuncName);
-
- }
-
- SkString transferFnFuncName;
- if (colorXformHelper->applyTransferFn()) {
- static const GrShaderVar gTransferFnArgs[] = { GrShaderVar("x", kHalf_GrSLType) };
- const char* coeffs = uniformHandler->getUniformCStr(colorXformHelper->transferFnUniform());
+ auto emitTFFunc = [=](const char* name, GrGLSLProgramDataManager::UniformHandle uniform) {
+ static const GrShaderVar gTFArgs[] = { GrShaderVar("x", kHalf_GrSLType) };
+ const char* coeffs = uniformHandler->getUniformCStr(uniform);
SkString body;
// Temporaries to make evaluation line readable
body.appendf("half G = %s[0];", coeffs);
@@ -139,18 +128,28 @@ void GrGLSLShaderBuilder::appendColorGamutXform(SkString* out,
body.append("half s = sign(x);");
body.append("x = abs(x);");
body.appendf("return s * ((x < D) ? (C * x) + F : pow(A * x + B, G) + E);");
- this->emitFunction(kHalf_GrSLType, "transfer_fn", SK_ARRAY_COUNT(gTransferFnArgs),
- gTransferFnArgs, body.c_str(), &transferFnFuncName);
+ SkString funcName;
+ this->emitFunction(kHalf_GrSLType, name, SK_ARRAY_COUNT(gTFArgs), gTFArgs, body.c_str(),
+ &funcName);
+ return funcName;
+ };
+
+ SkString srcTFFuncName;
+ if (colorXformHelper->applySrcTF()) {
+ srcTFFuncName = emitTFFunc("src_tf", colorXformHelper->srcTFUniform());
+ }
+
+ SkString dstTFFuncName;
+ if (colorXformHelper->applyDstTF()) {
+ dstTFFuncName = emitTFFunc("dst_tf", colorXformHelper->dstTFUniform());
}
SkString gamutXformFuncName;
if (colorXformHelper->applyGamutXform()) {
- // Our color is (r, g, b, a), but we want to multiply (r, g, b, 1) by our matrix, then
- // re-insert the original alpha.
static const GrShaderVar gGamutXformArgs[] = { GrShaderVar("color", kHalf4_GrSLType) };
const char* xform = uniformHandler->getUniformCStr(colorXformHelper->gamutXformUniform());
SkString body;
- body.appendf("color.rgb = clamp((%s * half4(color.rgb, 1.0)).rgb, 0.0, color.a);", xform);
+ body.appendf("color.rgb = (%s * color.rgb);", xform);
body.append("return color;");
this->emitFunction(kHalf4_GrSLType, "gamut_xform", SK_ARRAY_COUNT(gGamutXformArgs),
gGamutXformArgs, body.c_str(), &gamutXformFuncName);
@@ -160,19 +159,26 @@ void GrGLSLShaderBuilder::appendColorGamutXform(SkString* out,
{
static const GrShaderVar gColorXformArgs[] = { GrShaderVar("color", kHalf4_GrSLType) };
SkString body;
- if (colorXformHelper->applyInverseSRGB()) {
- body.appendf("color.r = %s(color.r);", inverseSrgbFuncName.c_str());
- body.appendf("color.g = %s(color.g);", inverseSrgbFuncName.c_str());
- body.appendf("color.b = %s(color.b);", inverseSrgbFuncName.c_str());
+ if (colorXformHelper->applyUnpremul()) {
+ body.append("half nonZeroAlpha = max(color.a, 0.00001);");
+ body.append("color = half4(color.rgb / nonZeroAlpha, nonZeroAlpha);");
}
- if (colorXformHelper->applyTransferFn()) {
- body.appendf("color.r = %s(color.r);", transferFnFuncName.c_str());
- body.appendf("color.g = %s(color.g);", transferFnFuncName.c_str());
- body.appendf("color.b = %s(color.b);", transferFnFuncName.c_str());
+ if (colorXformHelper->applySrcTF()) {
+ body.appendf("color.r = %s(color.r);", srcTFFuncName.c_str());
+ body.appendf("color.g = %s(color.g);", srcTFFuncName.c_str());
+ body.appendf("color.b = %s(color.b);", srcTFFuncName.c_str());
}
if (colorXformHelper->applyGamutXform()) {
body.appendf("color = %s(color);", gamutXformFuncName.c_str());
}
+ if (colorXformHelper->applyDstTF()) {
+ body.appendf("color.r = %s(color.r);", dstTFFuncName.c_str());
+ body.appendf("color.g = %s(color.g);", dstTFFuncName.c_str());
+ body.appendf("color.b = %s(color.b);", dstTFFuncName.c_str());
+ }
+ if (colorXformHelper->applyPremul()) {
+ body.append("color.rgb *= color.a;");
+ }
body.append("return color;");
SkString colorXformFuncName;
this->emitFunction(kHalf4_GrSLType, "color_xform", SK_ARRAY_COUNT(gColorXformArgs),
diff --git a/src/shaders/SkColorShader.cpp b/src/shaders/SkColorShader.cpp
index 482c731d00..b0387e5046 100644
--- a/src/shaders/SkColorShader.cpp
+++ b/src/shaders/SkColorShader.cpp
@@ -198,11 +198,11 @@ SkShader::GradientType SkColor4Shader::asAGradient(GradientInfo* info) const {
std::unique_ptr<GrFragmentProcessor> SkColor4Shader::asFragmentProcessor(
const GrFPArgs& args) const {
- auto colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(),
- args.fDstColorSpaceInfo->colorSpace());
+ auto xform = GrColorSpaceXform::MakeUnpremulToUnpremul(fColorSpace.get(),
+ args.fDstColorSpaceInfo->colorSpace());
GrColor4f color = GrColor4f::FromSkColor4f(fColor4);
- if (colorSpaceXform) {
- color = colorSpaceXform->clampedXform(color);
+ if (xform) {
+ color = xform->apply(color);
}
return GrConstColorProcessor::Make(color.premul(),
GrConstColorProcessor::InputMode::kModulateA);