Add color gamut xform helpers to GrGLSLShaderBuilder

New helper functions inject the necessary shader
function. Texture lookup functions can now insert
the gamut xform at the appropriate place, too.

As written, could be used to transform non-texture
colors (e.g. vertex colors) as well.

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2180803005

Review-Url: https://codereview.chromium.org/2180803005
This commit is contained in:
brianosman 2016-09-07 08:09:10 -07:00 committed by Commit bot
parent d2e39dbc6a
commit 77320dbabc
11 changed files with 166 additions and 27 deletions

View File

@ -8,6 +8,7 @@
#ifndef GrColorSpaceXform_DEFINED
#define GrColorSpaceXform_DEFINED
#include "SkImageInfo.h"
#include "SkRefCnt.h"
class SkColorSpace;
@ -18,15 +19,32 @@ class SkMatrix44;
*/
class GrColorSpaceXform : public SkRefCnt {
public:
GrColorSpaceXform(const SkMatrix44& srcToDst);
GrColorSpaceXform(const SkMatrix44& srcToDst, SkAlphaType srcAlphaType);
static sk_sp<GrColorSpaceXform> Make(SkColorSpace* src, SkColorSpace* dst);
static sk_sp<GrColorSpaceXform> Make(SkColorSpace* src, SkColorSpace* dst,
SkAlphaType srcAlphaType);
const float* srcToDst() { return fSrcToDst; }
SkAlphaType alphaType() const { return fSrcAlphaType; }
/**
* GrGLSLFragmentProcessor::GenKey() must call this and include the returned value in its
* computed key.
*/
static uint32_t XformKey(GrColorSpaceXform* xform) {
if (!xform) {
return 0;
}
// Code generation just depends on whether the alpha type is premul or not
return kPremul_SkAlphaType == xform->fSrcAlphaType ? 1 : 2;
}
private:
// We store the column-major form of the srcToDst matrix, for easy uploading to uniforms
float fSrcToDst[16];
// Alpha type of the source. If it's premul, we need special handling
SkAlphaType fSrcAlphaType;
};
#endif

View File

@ -34,11 +34,13 @@ static inline bool matrix_is_almost_identity(const SkMatrix44& m,
sk_float_almost_equals(m.getFloat(3, 3), 1.0f, tol);
}
GrColorSpaceXform::GrColorSpaceXform(const SkMatrix44& srcToDst) {
GrColorSpaceXform::GrColorSpaceXform(const SkMatrix44& srcToDst, SkAlphaType srcAlphaType)
: fSrcAlphaType(srcAlphaType) {
srcToDst.asColMajorf(fSrcToDst);
}
sk_sp<GrColorSpaceXform> GrColorSpaceXform::Make(SkColorSpace* src, SkColorSpace* dst) {
sk_sp<GrColorSpaceXform> GrColorSpaceXform::Make(SkColorSpace* src, SkColorSpace* dst,
SkAlphaType srcAlphaType) {
if (!src || !dst) {
// Invalid
return nullptr;
@ -59,5 +61,5 @@ sk_sp<GrColorSpaceXform> GrColorSpaceXform::Make(SkColorSpace* src, SkColorSpace
return nullptr;
}
return sk_make_sp<GrColorSpaceXform>(srcToDst);
return sk_make_sp<GrColorSpaceXform>(srcToDst, srcAlphaType);
}

View File

@ -424,7 +424,8 @@ sk_sp<GrFragmentProcessor> GrTextureAdjuster::createFragmentProcessor(
(domain.fLeft <= domain.fRight && domain.fTop <= domain.fBottom));
textureMatrix.postIDiv(texture->width(), texture->height());
sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(this->getColorSpace(),
dstColorSpace);
dstColorSpace,
this->alphaType());
return create_fp_for_domain_and_filter(texture, std::move(colorSpaceXform), textureMatrix,
domainMode, domain, filterOrNullForBicubic);
}
@ -506,7 +507,8 @@ sk_sp<GrFragmentProcessor> GrTextureMaker::createFragmentProcessor(
SkMatrix normalizedTextureMatrix = textureMatrix;
normalizedTextureMatrix.postIDiv(texture->width(), texture->height());
sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(this->getColorSpace(),
dstColorSpace);
dstColorSpace,
this->alphaType());
return create_fp_for_domain_and_filter(texture, std::move(colorSpaceXform),
normalizedTextureMatrix, domainMode, domain,
filterOrNullForBicubic);

View File

@ -1010,7 +1010,8 @@ void SkGpuDevice::drawBitmapTile(const SkBitmap& bitmap,
}
sk_sp<GrColorSpaceXform> colorSpaceXform =
GrColorSpaceXform::Make(bitmap.colorSpace(), fDrawContext->getColorSpace());
GrColorSpaceXform::Make(bitmap.colorSpace(), fDrawContext->getColorSpace(),
bitmap.alphaType());
SkScalar iw = 1.f / texture->width();
SkScalar ih = 1.f / texture->height();
@ -1134,8 +1135,12 @@ void SkGpuDevice::drawSpecial(const SkDraw& draw,
SkPaint tmpUnfiltered(paint);
tmpUnfiltered.setImageFilter(nullptr);
sk_sp<GrColorSpaceXform> colorSpaceXform =
GrColorSpaceXform::Make(result->getColorSpace(), fDrawContext->getColorSpace(),
result->alphaType());
GrPaint grPaint;
sk_sp<GrFragmentProcessor> fp(GrSimpleTextureEffect::Make(texture.get(), nullptr,
sk_sp<GrFragmentProcessor> fp(GrSimpleTextureEffect::Make(texture.get(),
std::move(colorSpaceXform),
SkMatrix::I()));
if (GrPixelConfigIsAlphaOnly(texture->config())) {
fp = GrFragmentProcessor::MulOutputByInputUnpremulColor(std::move(fp));

View File

@ -30,7 +30,7 @@ public:
GrProcessorKeyBuilder* b) {
const GrBicubicEffect& bicubicEffect = effect.cast<GrBicubicEffect>();
b->add32(GrTextureDomain::GLDomain::DomainKey(bicubicEffect.domain()));
b->add32(SkToInt(SkToBool(bicubicEffect.colorSpaceXform())));
b->add32(GrColorSpaceXform::XformKey(bicubicEffect.colorSpaceXform()));
}
protected:
@ -114,7 +114,9 @@ void GrGLBicubicEffect::emitCode(EmitArgs& args) {
SkString bicubicColor;
bicubicColor.printf("%s(%s, f.y, s0, s1, s2, s3)", cubicBlendName.c_str(), coeff);
if (colorSpaceHelper.getXformMatrix()) {
bicubicColor.appendf(" * %s", colorSpaceHelper.getXformMatrix());
SkString xformedColor;
fragBuilder->appendColorGamutXform(&xformedColor, bicubicColor.c_str(), &colorSpaceHelper);
bicubicColor.swap(xformedColor);
}
fragBuilder->codeAppendf("\t%s = %s;\n",
args.fOutputColor, (GrGLSLExpr4(bicubicColor.c_str()) *

View File

@ -8,23 +8,46 @@
#include "GrSimpleTextureEffect.h"
#include "GrInvariantOutput.h"
#include "GrTexture.h"
#include "glsl/GrGLSLColorSpaceXformHelper.h"
#include "glsl/GrGLSLFragmentProcessor.h"
#include "glsl/GrGLSLFragmentShaderBuilder.h"
class GrGLSimpleTextureEffect : public GrGLSLFragmentProcessor {
public:
void emitCode(EmitArgs& args) override {
const GrSimpleTextureEffect& textureEffect = args.fFp.cast<GrSimpleTextureEffect>();
GrGLSLColorSpaceXformHelper colorSpaceHelper(args.fUniformHandler,
textureEffect.colorSpaceXform(),
&fColorSpaceXformUni);
GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
fragBuilder->codeAppendf("%s = ", args.fOutputColor);
fragBuilder->appendTextureLookupAndModulate(args.fInputColor,
args.fTexSamplers[0],
args.fCoords[0].c_str(),
args.fCoords[0].getType());
args.fTexSamplers[0],
args.fCoords[0].c_str(),
args.fCoords[0].getType(),
&colorSpaceHelper);
fragBuilder->codeAppend(";");
}
static inline void GenKey(const GrProcessor& effect, const GrGLSLCaps&,
GrProcessorKeyBuilder* b) {
const GrSimpleTextureEffect& textureEffect = effect.cast<GrSimpleTextureEffect>();
b->add32(GrColorSpaceXform::XformKey(textureEffect.colorSpaceXform()));
}
protected:
void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& processor) override {
const GrSimpleTextureEffect& textureEffect = processor.cast<GrSimpleTextureEffect>();
if (SkToBool(textureEffect.colorSpaceXform())) {
pdman.setMatrix4f(fColorSpaceXformUni, textureEffect.colorSpaceXform()->srcToDst());
}
}
private:
typedef GrGLSLFragmentProcessor INHERITED;
UniformHandle fColorSpaceXformUni;
};
///////////////////////////////////////////////////////////////////////////////

View File

@ -30,6 +30,8 @@ public:
return str;
}
GrColorSpaceXform* colorSpaceXform() const { return fColorSpaceXform.get(); }
protected:
/** unfiltered, clamp mode */
GrSingleTextureEffect(GrTexture*, sk_sp<GrColorSpaceXform>, const SkMatrix&,

View File

@ -8,12 +8,13 @@
#ifndef GrGLSLColorSpaceXformHelper_DEFINED
#define GrGLSLColorSpaceXformHelper_DEFINED
#include "GrColorSpaceXform.h"
#include "GrGLSLUniformHandler.h"
class GrColorSpaceXform;
/**
* Stack helper class to assist with using GrColorSpaceXform within an FP's emitCode function.
* This injects the uniform declaration, and stores the information needed to generate correct
* gamut-transformation shader code.
*/
class GrGLSLColorSpaceXformHelper : public SkNoncopyable {
public:
@ -25,15 +26,18 @@ public:
*handle = uniformHandler->addUniform(kFragment_GrShaderFlag, kMat44f_GrSLType,
kDefault_GrSLPrecision, "ColorXform",
&fXformMatrix);
fAlphaType = colorSpaceXform->alphaType();
} else {
fXformMatrix = nullptr;
}
}
const char* getXformMatrix() const { return fXformMatrix; }
SkAlphaType alphaType() const { return fAlphaType; }
private:
const char* fXformMatrix;
SkAlphaType fAlphaType;
};
#endif

View File

@ -8,6 +8,7 @@
#include "GrSwizzle.h"
#include "glsl/GrGLSLShaderBuilder.h"
#include "glsl/GrGLSLCaps.h"
#include "glsl/GrGLSLColorSpaceXformHelper.h"
#include "glsl/GrGLSLShaderVar.h"
#include "glsl/GrGLSLSampler.h"
#include "glsl/GrGLSLProgramBuilder.h"
@ -98,17 +99,79 @@ void GrGLSLShaderBuilder::appendTextureLookup(SkString* out,
void GrGLSLShaderBuilder::appendTextureLookup(SamplerHandle samplerHandle,
const char* coordName,
GrSLType varyingType) {
this->appendTextureLookup(&this->code(), samplerHandle, coordName, varyingType);
GrSLType varyingType,
GrGLSLColorSpaceXformHelper* colorXformHelper) {
if (colorXformHelper && colorXformHelper->getXformMatrix()) {
// With a color gamut transform, we need to wrap the lookup in another function call
SkString lookup;
this->appendTextureLookup(&lookup, samplerHandle, coordName, varyingType);
this->appendColorGamutXform(lookup.c_str(), colorXformHelper);
} else {
this->appendTextureLookup(&this->code(), samplerHandle, coordName, varyingType);
}
}
void GrGLSLShaderBuilder::appendTextureLookupAndModulate(const char* modulation,
SamplerHandle samplerHandle,
const char* coordName,
GrSLType varyingType) {
void GrGLSLShaderBuilder::appendTextureLookupAndModulate(
const char* modulation,
SamplerHandle samplerHandle,
const char* coordName,
GrSLType varyingType,
GrGLSLColorSpaceXformHelper* colorXformHelper) {
SkString lookup;
this->appendTextureLookup(&lookup, samplerHandle, coordName, varyingType);
this->codeAppend((GrGLSLExpr4(modulation) * GrGLSLExpr4(lookup)).c_str());
if (colorXformHelper && colorXformHelper->getXformMatrix()) {
SkString xform;
this->appendColorGamutXform(&xform, lookup.c_str(), colorXformHelper);
this->codeAppend((GrGLSLExpr4(modulation) * GrGLSLExpr4(xform)).c_str());
} else {
this->codeAppend((GrGLSLExpr4(modulation) * GrGLSLExpr4(lookup)).c_str());
}
}
void GrGLSLShaderBuilder::appendColorGamutXform(SkString* out,
const char* srcColor,
GrGLSLColorSpaceXformHelper* colorXformHelper) {
// 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. The supplied srcColor is likely to be of the form
// "texture(...)", and we don't want to evaluate that twice.
//
// Worse: We can't do the transformation on premultiplied colors, so if the source is premul,
// we need to unpremul, transform, then multiply again. Anyways, we wrap all of the work in a
// function.
static const GrGLSLShaderVar gColorGamutXformArgs[] = {
GrGLSLShaderVar("color", kVec4f_GrSLType),
GrGLSLShaderVar("xform", kMat44f_GrSLType),
};
SkString functionBody;
if (kPremul_SkAlphaType == colorXformHelper->alphaType()) {
// Unpremultiply
functionBody.append("\tfloat nonZeroAlpha = max(color.a, 0.00001);\n"
"\tcolor.rgb = color.rgb / nonZeroAlpha;\n");
}
// Gamut xform, clamp to destination gamut
functionBody.append("\tcolor.rgb = clamp((vec4(color.rgb, 1.0) * xform).rgb, 0.0, 1.0);\n");
if (kPremul_SkAlphaType == colorXformHelper->alphaType()) {
// Re-multiply by alpha
functionBody.append("\tcolor.rgb = color.rgb * nonZeroAlpha;\n");
}
functionBody.append("\treturn color;");
SkString colorGamutXformFuncName;
this->emitFunction(kVec4f_GrSLType,
"colorGamutXform",
SK_ARRAY_COUNT(gColorGamutXformArgs),
gColorGamutXformArgs,
functionBody.c_str(),
&colorGamutXformFuncName);
out->appendf("%s(%s, %s)", colorGamutXformFuncName.c_str(), srcColor,
colorXformHelper->getXformMatrix());
}
void GrGLSLShaderBuilder::appendColorGamutXform(const char* srcColor,
GrGLSLColorSpaceXformHelper* colorXformHelper) {
SkString xform;
this->appendColorGamutXform(&xform, srcColor, colorXformHelper);
this->codeAppend(xform.c_str());
}
void GrGLSLShaderBuilder::appendTexelFetch(SkString* out,

View File

@ -15,6 +15,8 @@
#include <stdarg.h>
class GrGLSLColorSpaceXformHelper;
/**
base class for all shaders builders
*/
@ -37,7 +39,8 @@ public:
/** Version of above that appends the result to the shader code instead.*/
void appendTextureLookup(SamplerHandle,
const char* coordName,
GrSLType coordType = kVec2f_GrSLType);
GrSLType coordType = kVec2f_GrSLType,
GrGLSLColorSpaceXformHelper* colorXformHelper = nullptr);
/** Does the work of appendTextureLookup and modulates the result by modulation. The result is
@ -47,7 +50,18 @@ public:
void appendTextureLookupAndModulate(const char* modulation,
SamplerHandle,
const char* coordName,
GrSLType coordType = kVec2f_GrSLType);
GrSLType coordType = kVec2f_GrSLType,
GrGLSLColorSpaceXformHelper* colorXformHelper = nullptr);
/** Adds a helper function to facilitate color gamut transformation, and produces code that
returns the srcColor transformed into a new gamut (via multiplication by the xform from
colorXformHelper). Premultiplied sources are also handled correctly (colorXformHelper
determines if the source is premultipled or not). */
void appendColorGamutXform(SkString* out, const char* srcColor,
GrGLSLColorSpaceXformHelper* colorXformHelper);
/** Version of above that appends the result to the shader code instead. */
void appendColorGamutXform(const char* srcColor, GrGLSLColorSpaceXformHelper* colorXformHelper);
/** Fetches an unfiltered texel from a sampler at integer coordinates. coordExpr must match the
dimensionality of the sampler and must be within the sampler's range. coordExpr is emitted

View File

@ -214,11 +214,15 @@ sk_sp<GrFragmentProcessor> SkImageShader::asFragmentProcessor(const AsFPArgs& ar
return nullptr;
}
SkImageInfo info = as_IB(fImage)->onImageInfo();
sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(info.colorSpace(),
args.fDstColorSpace,
info.alphaType());
sk_sp<GrFragmentProcessor> inner;
if (doBicubic) {
inner = GrBicubicEffect::Make(texture, nullptr, matrix, tm);
inner = GrBicubicEffect::Make(texture, std::move(colorSpaceXform), matrix, tm);
} else {
inner = GrSimpleTextureEffect::Make(texture, nullptr, matrix, params);
inner = GrSimpleTextureEffect::Make(texture, std::move(colorSpaceXform), matrix, params);
}
if (GrPixelConfigIsAlphaOnly(texture->config())) {