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>
This commit is contained in:
parent
1bb47df4fc
commit
3567c14a41
@ -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
|
||||
|
@ -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;
|
||||
// TODO: Plumb source alpha type
|
||||
SkColorSpaceXformSteps steps(src, kPremul_SkAlphaType, dst);
|
||||
|
||||
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 steps.flags.mask() == 0 ? nullptr /* Noop transform */
|
||||
: sk_make_sp<GrColorSpaceXform>(steps);
|
||||
}
|
||||
|
||||
sk_sp<GrColorSpaceXform> GrColorSpaceXform::MakeUnpremulToUnpremul(SkColorSpace* src,
|
||||
SkColorSpace* dst) {
|
||||
// No transformation is performed in legacy mode, until SkColorSpaceXformCanvas is gone
|
||||
if (!dst) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (src == dst && (0 == flags)) {
|
||||
// Quick equality check - no conversion (or transfer function) needed in this case
|
||||
return nullptr;
|
||||
// Treat null sources as sRGB (safe because sRGB is a global singleton)
|
||||
if (!src) {
|
||||
src = SkColorSpace::MakeSRGB().get();
|
||||
}
|
||||
|
||||
const SkMatrix44* toXYZD50 = src->toXYZD50();
|
||||
const SkMatrix44* fromXYZD50 = dst->fromXYZD50();
|
||||
if (!toXYZD50 || !fromXYZD50) {
|
||||
// Unsupported colour spaces -- cannot specify gamut as a matrix
|
||||
return nullptr;
|
||||
}
|
||||
SkColorSpaceXformSteps steps = SkColorSpaceXformSteps::UnpremulToUnpremul(src, dst);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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 (SkToBool(a->fFlags & kApplyTransferFn_Flag) &&
|
||||
0 != memcmp(&a->fSrcTransferFn, &b->fSrcTransferFn, sizeof(SkColorSpaceTransferFn))) {
|
||||
if (a->fSteps.flags.linearize &&
|
||||
0 != memcmp(&a->fSteps.srcTF, &b->fSteps.srcTF, sizeof(a->fSteps.srcTF))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SkToBool(a->fFlags & kApplyGamutXform_Flag) && a->fGamutXform != b->fGamutXform) {
|
||||
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 (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)));
|
||||
}
|
||||
|
@ -10,71 +10,50 @@
|
||||
|
||||
#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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user