fmenozzi 2016-08-12 06:33:52 -07:00 committed by Commit bot
parent 1eda1eb203
commit 2a4959181f
3 changed files with 488 additions and 236 deletions

View File

@ -228,23 +228,6 @@ void SkGradientShaderBase::flatten(SkWriteBuffer& buffer) const {
desc.flatten(buffer); desc.flatten(buffer);
} }
SkGradientShaderBase::GpuColorType SkGradientShaderBase::getGpuColorType(SkColor colors[3]) const {
if (fColorCount <= 3) {
memcpy(colors, fOrigColors, fColorCount * sizeof(SkColor));
}
if (SkShader::kClamp_TileMode == fTileMode) {
if (2 == fColorCount) {
return kTwo_GpuColorType;
} else if (3 == fColorCount &&
(SkScalarAbs(
SkFixedToScalar(fRecs[1].fPos) - SK_ScalarHalf) < SK_Scalar1 / 1000)) {
return kThree_GpuColorType;
}
}
return kTexture_GpuColorType;
}
void SkGradientShaderBase::FlipGradientColors(SkColor* colorDst, Rec* recDst, void SkGradientShaderBase::FlipGradientColors(SkColor* colorDst, Rec* recDst,
SkColor* colorSrc, Rec* recSrc, SkColor* colorSrc, Rec* recSrc,
int count) { int count) {
@ -911,114 +894,191 @@ SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
#include "glsl/GrGLSLUniformHandler.h" #include "glsl/GrGLSLUniformHandler.h"
#include "SkGr.h" #include "SkGr.h"
GrGradientEffect::GLSLProcessor::GLSLProcessor() static inline bool close_to_one_half(const SkFixed& val) {
: fCachedYCoord(SK_ScalarMax) { return SkScalarNearlyEqual(SkFixedToScalar(val), SK_ScalarHalf);
}
static inline int color_type_to_color_count(GrGradientEffect::ColorType colorType) {
switch (colorType) {
#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS
case GrGradientEffect::kHardStopCentered_ColorType:
return 4;
case GrGradientEffect::kHardStopLeftEdged_ColorType:
case GrGradientEffect::kHardStopRightEdged_ColorType:
return 3;
#endif
case GrGradientEffect::kTwo_ColorType:
return 2;
case GrGradientEffect::kThree_ColorType:
return 3;
case GrGradientEffect::kTexture_ColorType:
return 0;
}
SkDEBUGFAIL("Unhandled ColorType in color_type_to_color_count()");
return -1;
}
GrGradientEffect::ColorType GrGradientEffect::determineColorType(
const SkGradientShaderBase& shader) {
#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS
if (shader.fOrigPos) {
if (4 == shader.fColorCount) {
if (SkScalarNearlyEqual(shader.fOrigPos[0], 0.0f) &&
SkScalarNearlyEqual(shader.fOrigPos[1], 0.5f) &&
SkScalarNearlyEqual(shader.fOrigPos[2], 0.5f) &&
SkScalarNearlyEqual(shader.fOrigPos[3], 1.0f)) {
return kHardStopCentered_ColorType;
}
} else if (3 == shader.fColorCount) {
if (SkScalarNearlyEqual(shader.fOrigPos[0], 0.0f) &&
SkScalarNearlyEqual(shader.fOrigPos[1], 0.0f) &&
SkScalarNearlyEqual(shader.fOrigPos[2], 1.0f)) {
return kHardStopLeftEdged_ColorType;
} else if (SkScalarNearlyEqual(shader.fOrigPos[0], 0.0f) &&
SkScalarNearlyEqual(shader.fOrigPos[1], 1.0f) &&
SkScalarNearlyEqual(shader.fOrigPos[2], 1.0f)) {
return kHardStopRightEdged_ColorType;
}
}
}
#endif
if (SkShader::kClamp_TileMode == shader.getTileMode()) {
if (2 == shader.fColorCount) {
return kTwo_ColorType;
} else if (3 == shader.fColorCount &&
close_to_one_half(shader.getRecs()[1].fPos)) {
return kThree_ColorType;
}
}
return kTexture_ColorType;
} }
void GrGradientEffect::GLSLProcessor::emitUniforms(GrGLSLUniformHandler* uniformHandler, void GrGradientEffect::GLSLProcessor::emitUniforms(GrGLSLUniformHandler* uniformHandler,
const GrGradientEffect& ge) { const GrGradientEffect& ge) {
if (int colorCount = color_type_to_color_count(ge.getColorType())) {
if (SkGradientShaderBase::kTwo_GpuColorType == ge.getColorType()) { // 2 Color case fColorsUni = uniformHandler->addUniformArray(kFragment_GrShaderFlag,
fColorStartUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kVec4f_GrSLType,
kVec4f_GrSLType, kDefault_GrSLPrecision, kDefault_GrSLPrecision,
"GradientStartColor"); "Colors",
fColorEndUni = uniformHandler->addUniform(kFragment_GrShaderFlag, colorCount);
kVec4f_GrSLType, kDefault_GrSLPrecision, } else {
"GradientEndColor");
} else if (SkGradientShaderBase::kThree_GpuColorType == ge.getColorType()) { // 3 Color Case
fColorStartUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
kVec4f_GrSLType, kDefault_GrSLPrecision,
"GradientStartColor");
fColorMidUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
kVec4f_GrSLType, kDefault_GrSLPrecision,
"GradientMidColor");
fColorEndUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
kVec4f_GrSLType, kDefault_GrSLPrecision,
"GradientEndColor");
} else { // if not a fast case
fFSYUni = uniformHandler->addUniform(kFragment_GrShaderFlag, fFSYUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
kFloat_GrSLType, kDefault_GrSLPrecision, kFloat_GrSLType, kDefault_GrSLPrecision,
"GradientYCoordFS"); "GradientYCoordFS");
} }
} }
static inline void set_color_uni(const GrGLSLProgramDataManager& pdman, static inline void set_after_interp_color_uni_array(const GrGLSLProgramDataManager& pdman,
const GrGLSLProgramDataManager::UniformHandle uni, const GrGLSLProgramDataManager::UniformHandle uni,
const SkColor* color) { const SkTDArray<SkColor>& colors) {
pdman.set4f(uni, int count = colors.count();
SkColorGetR(*color) / 255.f, constexpr int kSmallCount = 10;
SkColorGetG(*color) / 255.f,
SkColorGetB(*color) / 255.f, SkAutoSTArray<4*kSmallCount, float> vals(4*count);
SkColorGetA(*color) / 255.f);
for (int i = 0; i < colors.count(); i++) {
// RGBA
vals[4*i + 0] = SkColorGetR(colors[i]) / 255.f;
vals[4*i + 1] = SkColorGetG(colors[i]) / 255.f;
vals[4*i + 2] = SkColorGetB(colors[i]) / 255.f;
vals[4*i + 3] = SkColorGetA(colors[i]) / 255.f;
}
pdman.set4fv(uni, colors.count(), vals.get());
} }
static inline void set_mul_color_uni(const GrGLSLProgramDataManager& pdman, static inline void set_before_interp_color_uni_array(const GrGLSLProgramDataManager& pdman,
const GrGLSLProgramDataManager::UniformHandle uni, const GrGLSLProgramDataManager::UniformHandle uni,
const SkColor* color){ const SkTDArray<SkColor>& colors) {
float a = SkColorGetA(*color) / 255.f; int count = colors.count();
float aDiv255 = a / 255.f; constexpr int kSmallCount = 10;
pdman.set4f(uni,
SkColorGetR(*color) * aDiv255, SkAutoSTArray<4*kSmallCount, float> vals(4*count);
SkColorGetG(*color) * aDiv255,
SkColorGetB(*color) * aDiv255, for (int i = 0; i < count; i++) {
a); float a = SkColorGetA(colors[i]) / 255.f;
float aDiv255 = a / 255.f;
// RGBA
vals[4*i + 0] = SkColorGetR(colors[i]) * aDiv255;
vals[4*i + 1] = SkColorGetG(colors[i]) * aDiv255;
vals[4*i + 2] = SkColorGetB(colors[i]) * aDiv255;
vals[4*i + 3] = a;
}
pdman.set4fv(uni, count, vals.get());
} }
void GrGradientEffect::GLSLProcessor::onSetData(const GrGLSLProgramDataManager& pdman, void GrGradientEffect::GLSLProcessor::onSetData(const GrGLSLProgramDataManager& pdman,
const GrProcessor& processor) { const GrProcessor& processor) {
const GrGradientEffect& e = processor.cast<GrGradientEffect>(); const GrGradientEffect& e = processor.cast<GrGradientEffect>();
switch (e.getColorType()) {
#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS
case GrGradientEffect::kHardStopCentered_ColorType:
case GrGradientEffect::kHardStopLeftEdged_ColorType:
case GrGradientEffect::kHardStopRightEdged_ColorType:
#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);
} else {
set_after_interp_color_uni_array(pdman, fColorsUni, e.fColors);
}
if (SkGradientShaderBase::kTwo_GpuColorType == e.getColorType()){ break;
if (GrGradientEffect::kBeforeInterp_PremulType == e.getPremulType()) {
set_mul_color_uni(pdman, fColorStartUni, e.getColors(0));
set_mul_color_uni(pdman, fColorEndUni, e.getColors(1));
} else {
set_color_uni(pdman, fColorStartUni, e.getColors(0));
set_color_uni(pdman, fColorEndUni, e.getColors(1));
} }
} else if (SkGradientShaderBase::kThree_GpuColorType == e.getColorType()){ case GrGradientEffect::kTexture_ColorType: {
SkScalar yCoord = e.getYCoord();
if (GrGradientEffect::kBeforeInterp_PremulType == e.getPremulType()) { if (yCoord != fCachedYCoord) {
set_mul_color_uni(pdman, fColorStartUni, e.getColors(0)); pdman.set1f(fFSYUni, yCoord);
set_mul_color_uni(pdman, fColorMidUni, e.getColors(1)); fCachedYCoord = yCoord;
set_mul_color_uni(pdman, fColorEndUni, e.getColors(2)); }
} else { break;
set_color_uni(pdman, fColorStartUni, e.getColors(0));
set_color_uni(pdman, fColorMidUni, e.getColors(1));
set_color_uni(pdman, fColorEndUni, e.getColors(2));
}
} else {
SkScalar yCoord = e.getYCoord();
if (yCoord != fCachedYCoord) {
pdman.set1f(fFSYUni, yCoord);
fCachedYCoord = yCoord;
} }
} }
} }
uint32_t GrGradientEffect::GLSLProcessor::GenBaseGradientKey(const GrProcessor& processor) { uint32_t GrGradientEffect::GLSLProcessor::GenBaseGradientKey(const GrProcessor& processor) {
const GrGradientEffect& e = processor.cast<GrGradientEffect>(); const GrGradientEffect& e = processor.cast<GrGradientEffect>();
uint32_t key = 0; uint32_t key = 0;
if (SkGradientShaderBase::kTwo_GpuColorType == e.getColorType()) {
key |= kTwoColorKey;
} else if (SkGradientShaderBase::kThree_GpuColorType == e.getColorType()) {
key |= kThreeColorKey;
}
if (GrGradientEffect::kBeforeInterp_PremulType == e.getPremulType()) { if (GrGradientEffect::kBeforeInterp_PremulType == e.getPremulType()) {
key |= kPremulBeforeInterpKey; key |= kPremulBeforeInterpKey;
} }
if (GrGradientEffect::kTwo_ColorType == e.getColorType()) {
key |= kTwoColorKey;
} else if (GrGradientEffect::kThree_ColorType == e.getColorType()) {
key |= kThreeColorKey;
}
#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS
else if (GrGradientEffect::kHardStopCentered_ColorType == e.getColorType()) {
key |= kHardStopCenteredKey;
} else if (GrGradientEffect::kHardStopLeftEdged_ColorType == e.getColorType()) {
key |= kHardStopZeroZeroOneKey;
} else if (GrGradientEffect::kHardStopRightEdged_ColorType == e.getColorType()) {
key |= kHardStopZeroOneOneKey;
}
if (SkShader::TileMode::kClamp_TileMode == e.fTileMode) {
key |= kClampTileMode;
} else if (SkShader::TileMode::kRepeat_TileMode == e.fTileMode) {
key |= kRepeatTileMode;
} else {
key |= kMirrorTileMode;
}
#endif
return key; return key;
} }
@ -1030,56 +1090,183 @@ void GrGradientEffect::GLSLProcessor::emitColor(GrGLSLFPFragmentBuilder* fragBui
const char* outputColor, const char* outputColor,
const char* inputColor, const char* inputColor,
const SamplerHandle* texSamplers) { const SamplerHandle* texSamplers) {
if (SkGradientShaderBase::kTwo_GpuColorType == ge.getColorType()){ switch (ge.getColorType()) {
fragBuilder->codeAppendf("\tvec4 colorTemp = mix(%s, %s, clamp(%s, 0.0, 1.0));\n", #if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS
uniformHandler->getUniformVariable(fColorStartUni).c_str(), case kHardStopCentered_ColorType: {
uniformHandler->getUniformVariable(fColorEndUni).c_str(), const char* t = gradientTValue;
gradientTValue); const char* colors = uniformHandler->getUniformCStr(fColorsUni);
// Note that we could skip this step if both colors are known to be opaque. Two
// considerations: fragBuilder->codeAppendf("float clamp_t = clamp(%s, 0.0, 1.0);", t);
// The gradient SkShader reporting opaque is more restrictive than necessary in the two pt
// case. Make sure the key reflects this optimization (and note that it can use the same // Account for tile mode
// shader as thekBeforeIterp case). This same optimization applies to the 3 color case if (SkShader::kRepeat_TileMode == ge.fTileMode) {
// below. fragBuilder->codeAppendf("clamp_t = fract(%s);", t);
if (GrGradientEffect::kAfterInterp_PremulType == ge.getPremulType()) { } else if (SkShader::kMirror_TileMode == ge.fTileMode) {
fragBuilder->codeAppend("\tcolorTemp.rgb *= colorTemp.a;\n"); fragBuilder->codeAppendf("if (%s < 0.0 || %s > 1.0) {", t, t);
fragBuilder->codeAppendf(" if (mod(floor(%s), 2.0) == 0.0) {", t);
fragBuilder->codeAppendf(" clamp_t = fract(%s);", t);
fragBuilder->codeAppendf(" } else {");
fragBuilder->codeAppendf(" clamp_t = 1.0 - fract(%s);", t);
fragBuilder->codeAppendf(" }");
fragBuilder->codeAppendf("}");
}
// Calculate color
fragBuilder->codeAppendf("float relative_t = fract(2.0 * clamp_t);");
if (SkShader::kClamp_TileMode == ge.fTileMode) {
fragBuilder->codeAppendf("relative_t += step(1.0, %s);", t);
}
fragBuilder->codeAppendf("vec4 colorTemp = mix(%s[0], %s[1], relative_t);", colors,
colors);
fragBuilder->codeAppendf("if (clamp_t >= 0.5) {");
fragBuilder->codeAppendf(" colorTemp = mix(%s[2], %s[3], relative_t);", colors,
colors);
fragBuilder->codeAppendf("}");
if (GrGradientEffect::kAfterInterp_PremulType == ge.getPremulType()) {
fragBuilder->codeAppend("colorTemp.rgb *= colorTemp.a;");
}
fragBuilder->codeAppendf("%s = %s;", outputColor,
(GrGLSLExpr4(inputColor) * GrGLSLExpr4("colorTemp")).c_str());
break;
} }
fragBuilder->codeAppendf("\t%s = %s;\n", outputColor, case kHardStopLeftEdged_ColorType: {
(GrGLSLExpr4(inputColor) * GrGLSLExpr4("colorTemp")).c_str()); const char* t = gradientTValue;
} else if (SkGradientShaderBase::kThree_GpuColorType == ge.getColorType()) { const char* colors = uniformHandler->getUniformCStr(fColorsUni);
fragBuilder->codeAppendf("\tfloat oneMinus2t = 1.0 - (2.0 * (%s));\n",
gradientTValue); fragBuilder->codeAppendf("float clamp_t = clamp(%s, 0.0, 1.0);", t);
fragBuilder->codeAppendf("\tvec4 colorTemp = clamp(oneMinus2t, 0.0, 1.0) * %s;\n",
uniformHandler->getUniformVariable(fColorStartUni).c_str()); // Account for tile mode
if (!glslCaps->canUseMinAndAbsTogether()) { if (SkShader::kRepeat_TileMode == ge.fTileMode) {
// The Tegra3 compiler will sometimes never return if we have fragBuilder->codeAppendf("clamp_t = fract(%s);", t);
// min(abs(oneMinus2t), 1.0), or do the abs first in a separate expression. } else if (SkShader::kMirror_TileMode == ge.fTileMode) {
fragBuilder->codeAppend("\tfloat minAbs = abs(oneMinus2t);\n"); fragBuilder->codeAppendf("if (%s < 0.0 || %s > 1.0) {", t, t);
fragBuilder->codeAppend("\tminAbs = minAbs > 1.0 ? 1.0 : minAbs;\n"); fragBuilder->codeAppendf(" if (mod(floor(%s), 2.0) == 0.0) {", t);
fragBuilder->codeAppendf("\tcolorTemp += (1.0 - minAbs) * %s;\n", fragBuilder->codeAppendf(" clamp_t = fract(%s);", t);
uniformHandler->getUniformVariable(fColorMidUni).c_str()); fragBuilder->codeAppendf(" } else {");
} else { fragBuilder->codeAppendf(" clamp_t = 1.0 - fract(%s);", t);
fragBuilder->codeAppendf("\tcolorTemp += (1.0 - min(abs(oneMinus2t), 1.0)) * %s;\n", fragBuilder->codeAppendf(" }");
uniformHandler->getUniformVariable(fColorMidUni).c_str()); fragBuilder->codeAppendf("}");
} }
fragBuilder->codeAppendf("\tcolorTemp += clamp(-oneMinus2t, 0.0, 1.0) * %s;\n",
uniformHandler->getUniformVariable(fColorEndUni).c_str()); fragBuilder->codeAppendf("vec4 colorTemp = mix(%s[1], %s[2], clamp_t);", colors,
if (GrGradientEffect::kAfterInterp_PremulType == ge.getPremulType()) { colors);
fragBuilder->codeAppend("\tcolorTemp.rgb *= colorTemp.a;\n"); if (SkShader::kClamp_TileMode == ge.fTileMode) {
fragBuilder->codeAppendf("if (%s < 0.0) {", t);
fragBuilder->codeAppendf(" colorTemp = %s[0];", colors);
fragBuilder->codeAppendf("}");
}
if (GrGradientEffect::kAfterInterp_PremulType == ge.getPremulType()) {
fragBuilder->codeAppend("colorTemp.rgb *= colorTemp.a;");
}
fragBuilder->codeAppendf("%s = %s;", outputColor,
(GrGLSLExpr4(inputColor) * GrGLSLExpr4("colorTemp")).c_str());
break;
} }
fragBuilder->codeAppendf("\t%s = %s;\n", outputColor, case kHardStopRightEdged_ColorType: {
(GrGLSLExpr4(inputColor) * GrGLSLExpr4("colorTemp")).c_str()); const char* t = gradientTValue;
} else { const char* colors = uniformHandler->getUniformCStr(fColorsUni);
fragBuilder->codeAppendf("\tvec2 coord = vec2(%s, %s);\n",
gradientTValue, fragBuilder->codeAppendf("float clamp_t = clamp(%s, 0.0, 1.0);", t);
uniformHandler->getUniformVariable(fFSYUni).c_str());
fragBuilder->codeAppendf("\t%s = ", outputColor); // Account for tile mode
fragBuilder->appendTextureLookupAndModulate(inputColor, if (SkShader::kRepeat_TileMode == ge.fTileMode) {
texSamplers[0], fragBuilder->codeAppendf("clamp_t = fract(%s);", t);
"coord"); } else if (SkShader::kMirror_TileMode == ge.fTileMode) {
fragBuilder->codeAppend(";\n"); fragBuilder->codeAppendf("if (%s < 0.0 || %s > 1.0) {", t, t);
fragBuilder->codeAppendf(" if (mod(floor(%s), 2.0) == 0.0) {", t);
fragBuilder->codeAppendf(" clamp_t = fract(%s);", t);
fragBuilder->codeAppendf(" } else {");
fragBuilder->codeAppendf(" clamp_t = 1.0 - fract(%s);", t);
fragBuilder->codeAppendf(" }");
fragBuilder->codeAppendf("}");
}
fragBuilder->codeAppendf("vec4 colorTemp = mix(%s[0], %s[1], clamp_t);", colors,
colors);
if (SkShader::kClamp_TileMode == ge.fTileMode) {
fragBuilder->codeAppendf("if (%s > 1.0) {", t);
fragBuilder->codeAppendf(" colorTemp = %s[2];", colors);
fragBuilder->codeAppendf("}");
}
if (GrGradientEffect::kAfterInterp_PremulType == ge.getPremulType()) {
fragBuilder->codeAppend("colorTemp.rgb *= colorTemp.a;");
}
fragBuilder->codeAppendf("%s = %s;", outputColor,
(GrGLSLExpr4(inputColor) * GrGLSLExpr4("colorTemp")).c_str());
break;
}
#endif
case kTwo_ColorType: {
const char* t = gradientTValue;
const char* colors = uniformHandler->getUniformCStr(fColorsUni);
fragBuilder->codeAppendf("vec4 colorTemp = mix(%s[0], %s[1], clamp(%s, 0.0, 1.0));",
colors, colors, t);
// We could skip this step if both colors are known to be opaque. Two
// considerations:
// The gradient SkShader reporting opaque is more restrictive than necessary in the two
// pt case. Make sure the key reflects this optimization (and note that it can use the
// same shader as thekBeforeIterp case). This same optimization applies to the 3 color
// case below.
if (GrGradientEffect::kAfterInterp_PremulType == ge.getPremulType()) {
fragBuilder->codeAppend("colorTemp.rgb *= colorTemp.a;");
}
fragBuilder->codeAppendf("%s = %s;", outputColor,
(GrGLSLExpr4(inputColor) * GrGLSLExpr4("colorTemp")).c_str());
break;
}
case kThree_ColorType: {
const char* t = gradientTValue;
const char* colors = uniformHandler->getUniformCStr(fColorsUni);
fragBuilder->codeAppendf("float oneMinus2t = 1.0 - (2.0 * %s);", t);
fragBuilder->codeAppendf("vec4 colorTemp = clamp(oneMinus2t, 0.0, 1.0) * %s[0];",
colors);
if (!glslCaps->canUseMinAndAbsTogether()) {
// The Tegra3 compiler will sometimes never return if we have
// min(abs(oneMinus2t), 1.0), or do the abs first in a separate expression.
fragBuilder->codeAppendf("float minAbs = abs(oneMinus2t);");
fragBuilder->codeAppendf("minAbs = minAbs > 1.0 ? 1.0 : minAbs;");
fragBuilder->codeAppendf("colorTemp += (1.0 - minAbs) * %s[1];", colors);
} else {
fragBuilder->codeAppendf("colorTemp += (1.0 - min(abs(oneMinus2t), 1.0)) * %s[1];",
colors);
}
fragBuilder->codeAppendf("colorTemp += clamp(-oneMinus2t, 0.0, 1.0) * %s[2];", colors);
if (GrGradientEffect::kAfterInterp_PremulType == ge.getPremulType()) {
fragBuilder->codeAppend("colorTemp.rgb *= colorTemp.a;");
}
fragBuilder->codeAppendf("%s = %s;", outputColor,
(GrGLSLExpr4(inputColor) * GrGLSLExpr4("colorTemp")).c_str());
break;
}
case kTexture_ColorType: {
const char* fsyuni = uniformHandler->getUniformCStr(fFSYUni);
fragBuilder->codeAppendf("vec2 coord = vec2(%s, %s);", gradientTValue, fsyuni);
fragBuilder->codeAppendf("%s = ", outputColor);
fragBuilder->appendTextureLookupAndModulate(inputColor, texSamplers[0], "coord");
fragBuilder->codeAppend(";");
break;
}
} }
} }
@ -1092,56 +1279,87 @@ GrGradientEffect::GrGradientEffect(GrContext* ctx,
fIsOpaque = shader.isOpaque(); fIsOpaque = shader.isOpaque();
fColorType = shader.getGpuColorType(&fColors[0]); fColorType = this->determineColorType(shader);
// The two and three color specializations do not currently support tiling. if (kTexture_ColorType != fColorType) {
if (SkGradientShaderBase::kTwo_GpuColorType == fColorType || if (shader.fOrigColors) {
SkGradientShaderBase::kThree_GpuColorType == fColorType) { fColors = SkTDArray<SkColor>(shader.fOrigColors, shader.fColorCount);
fRow = -1;
if (SkGradientShader::kInterpolateColorsInPremul_Flag & shader.getGradFlags()) {
fPremulType = kBeforeInterp_PremulType;
} else {
fPremulType = kAfterInterp_PremulType;
} }
fCoordTransform.reset(kCoordSet, matrix);
} else {
// doesn't matter how this is set, just be consistent because it is part of the effect key.
fPremulType = kBeforeInterp_PremulType;
SkBitmap bitmap;
shader.getGradientTableBitmap(&bitmap);
GrTextureStripAtlas::Desc desc; #if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS
desc.fWidth = bitmap.width(); if (shader.fOrigPos) {
desc.fHeight = 32; fPositions = SkTDArray<SkScalar>(shader.fOrigPos, shader.fColorCount);
desc.fRowHeight = bitmap.height();
desc.fContext = ctx;
desc.fConfig = SkImageInfo2GrPixelConfig(bitmap.info(), *ctx->caps());
fAtlas = GrTextureStripAtlas::GetAtlas(desc);
SkASSERT(fAtlas);
// We always filter the gradient table. Each table is one row of a texture, always y-clamp.
GrTextureParams params;
params.setFilterMode(GrTextureParams::kBilerp_FilterMode);
params.setTileModeX(tileMode);
fRow = fAtlas->lockRow(bitmap);
if (-1 != fRow) {
fYCoord = fAtlas->getYOffset(fRow) + SK_ScalarHalf * fAtlas->getNormalizedTexelHeight();
fCoordTransform.reset(kCoordSet, matrix, fAtlas->getTexture(), params.filterMode());
fTextureAccess.reset(fAtlas->getTexture(), params);
} else {
SkAutoTUnref<GrTexture> texture(
GrRefCachedBitmapTexture(ctx, bitmap, params, SkSourceGammaTreatment::kRespect));
if (!texture) {
return;
}
fCoordTransform.reset(kCoordSet, matrix, texture, params.filterMode());
fTextureAccess.reset(texture, params);
fYCoord = SK_ScalarHalf;
} }
this->addTextureAccess(&fTextureAccess);
fTileMode = tileMode;
#endif
} }
switch (fColorType) {
// The two and three color specializations do not currently support tiling.
case kTwo_ColorType:
case kThree_ColorType:
#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS
case kHardStopLeftEdged_ColorType:
case kHardStopRightEdged_ColorType:
case kHardStopCentered_ColorType:
#endif
fRow = -1;
if (SkGradientShader::kInterpolateColorsInPremul_Flag & shader.getGradFlags()) {
fPremulType = kBeforeInterp_PremulType;
} else {
fPremulType = kAfterInterp_PremulType;
}
fCoordTransform.reset(kCoordSet, matrix);
break;
case kTexture_ColorType:
// doesn't matter how this is set, just be consistent because it is part of the
// effect key.
fPremulType = kBeforeInterp_PremulType;
SkBitmap bitmap;
shader.getGradientTableBitmap(&bitmap);
GrTextureStripAtlas::Desc desc;
desc.fWidth = bitmap.width();
desc.fHeight = 32;
desc.fRowHeight = bitmap.height();
desc.fContext = ctx;
desc.fConfig = SkImageInfo2GrPixelConfig(bitmap.info(), *ctx->caps());
fAtlas = GrTextureStripAtlas::GetAtlas(desc);
SkASSERT(fAtlas);
// We always filter the gradient table. Each table is one row of a texture, always
// y-clamp.
GrTextureParams params;
params.setFilterMode(GrTextureParams::kBilerp_FilterMode);
params.setTileModeX(tileMode);
fRow = fAtlas->lockRow(bitmap);
if (-1 != fRow) {
fYCoord = fAtlas->getYOffset(fRow)+SK_ScalarHalf*fAtlas->getNormalizedTexelHeight();
fCoordTransform.reset(kCoordSet, matrix, fAtlas->getTexture(), params.filterMode());
fTextureAccess.reset(fAtlas->getTexture(), params);
} else {
SkAutoTUnref<GrTexture> texture(
GrRefCachedBitmapTexture(ctx, bitmap, params,
SkSourceGammaTreatment::kRespect));
if (!texture) {
return;
}
fCoordTransform.reset(kCoordSet, matrix, texture, params.filterMode());
fTextureAccess.reset(texture, params);
fYCoord = SK_ScalarHalf;
}
this->addTextureAccess(&fTextureAccess);
break;
}
this->addCoordTransform(&fCoordTransform); this->addCoordTransform(&fCoordTransform);
} }
@ -1152,30 +1370,27 @@ GrGradientEffect::~GrGradientEffect() {
} }
bool GrGradientEffect::onIsEqual(const GrFragmentProcessor& processor) const { bool GrGradientEffect::onIsEqual(const GrFragmentProcessor& processor) const {
const GrGradientEffect& s = processor.cast<GrGradientEffect>(); const GrGradientEffect& ge = processor.cast<GrGradientEffect>();
if (this->fColorType == s.getColorType()){ if (this->fColorType == ge.getColorType()) {
if (kTexture_ColorType == fColorType) {
if (SkGradientShaderBase::kTwo_GpuColorType == fColorType) { if (fYCoord != ge.getYCoord()) {
if (this->getPremulType() != s.getPremulType() ||
*this->getColors(0) != *s.getColors(0) ||
*this->getColors(1) != *s.getColors(1)) {
return false;
}
} else if (SkGradientShaderBase::kThree_GpuColorType == fColorType) {
if (this->getPremulType() != s.getPremulType() ||
*this->getColors(0) != *s.getColors(0) ||
*this->getColors(1) != *s.getColors(1) ||
*this->getColors(2) != *s.getColors(2)) {
return false; return false;
} }
} else { } else {
if (fYCoord != s.getYCoord()) { if (this->getPremulType() != ge.getPremulType() ||
this->fColors.count() != ge.fColors.count()) {
return false; return false;
} }
for (int i = 0; i < this->fColors.count(); i++) {
if (*this->getColors(i) != *ge.getColors(i)) {
return false;
}
}
} }
SkASSERT(this->useAtlas() == s.useAtlas()); SkASSERT(this->useAtlas() == ge.useAtlas());
return true; return true;
} }

View File

@ -19,6 +19,10 @@
#include "SkShader.h" #include "SkShader.h"
#include "SkOnce.h" #include "SkOnce.h"
#if SK_SUPPORT_GPU
#define GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS 1
#endif
static inline void sk_memset32_dither(uint32_t dst[], uint32_t v0, uint32_t v1, static inline void sk_memset32_dither(uint32_t dst[], uint32_t v0, uint32_t v1,
int count) { int count) {
if (count > 0) { if (count > 0) {
@ -128,7 +132,8 @@ public:
bool getDither() const { return fCacheDither; } bool getDither() const { return fCacheDither; }
private: private:
// Working pointers. If either is nullptr, we need to recompute the corresponding cache values. // Working pointers. If either is nullptr, we need to recompute the corresponding
// cache values.
uint16_t* fCache16; uint16_t* fCache16;
SkPMColor* fCache32; SkPMColor* fCache32;
@ -197,17 +202,6 @@ public:
kDitherStride16 = kCache16Count, kDitherStride16 = kCache16Count,
}; };
enum GpuColorType {
kTwo_GpuColorType,
kThree_GpuColorType, // Symmetric three color
kTexture_GpuColorType
};
// Determines and returns the gradient is a two color gradient, symmetric three color gradient
// or other (texture gradient). If it is two or symmetric three color, the colors array will
// also be filled with the gradient colors
GpuColorType getGpuColorType(SkColor colors[3]) const;
uint32_t getGradFlags() const { return fGradFlags; } uint32_t getGradFlags() const { return fGradFlags; }
protected: protected:
@ -220,7 +214,6 @@ protected:
const SkMatrix fPtsToUnit; const SkMatrix fPtsToUnit;
TileMode fTileMode; TileMode fTileMode;
TileProc fTileProc; TileProc fTileProc;
int fColorCount;
uint8_t fGradFlags; uint8_t fGradFlags;
struct Rec { struct Rec {
@ -254,9 +247,15 @@ private:
public: public:
SkColor* fOrigColors; // original colors, before modulation by paint in context. SkColor* fOrigColors; // original colors, before modulation by paint in context.
SkScalar* fOrigPos; // original positions SkScalar* fOrigPos; // original positions
int fColorCount;
SkTArray<sk_sp<SkShader>> fSubGradients;
bool colorsAreOpaque() const { return fColorsAreOpaque; } bool colorsAreOpaque() const { return fColorsAreOpaque; }
TileMode getTileMode() const { return fTileMode; }
Rec* getRecs() const { return fRecs; }
private: private:
bool fColorsAreOpaque; bool fColorsAreOpaque;
@ -336,9 +335,30 @@ public:
virtual ~GrGradientEffect(); virtual ~GrGradientEffect();
bool useAtlas() const { return SkToBool(-1 != fRow); } bool useAtlas() const { return SkToBool(-1 != fRow); }
SkScalar getYCoord() const { return fYCoord; }; SkScalar getYCoord() const { return fYCoord; }
SkGradientShaderBase::GpuColorType getColorType() const { return fColorType; } enum ColorType {
kTwo_ColorType,
kThree_ColorType, // Symmetric three color
kTexture_ColorType,
#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS
kHardStopCentered_ColorType, // 0, 0.5, 0.5, 1
kHardStopLeftEdged_ColorType, // 0, 0, 1
kHardStopRightEdged_ColorType, // 0, 1, 1
#endif
};
ColorType getColorType() const { return fColorType; }
// Determines the type of gradient, one of:
// - Two-color
// - Symmetric three-color
// - Texture
// - Centered hard stop
// - Left-edged hard stop
// - Right-edged hard stop
ColorType determineColorType(const SkGradientShaderBase& shader);
enum PremulType { enum PremulType {
kBeforeInterp_PremulType, kBeforeInterp_PremulType,
@ -348,8 +368,8 @@ public:
PremulType getPremulType() const { return fPremulType; } PremulType getPremulType() const { return fPremulType; }
const SkColor* getColors(int pos) const { const SkColor* getColors(int pos) const {
SkASSERT(fColorType != SkGradientShaderBase::kTexture_GpuColorType); SkASSERT(fColorType != kTexture_ColorType);
SkASSERT((pos-1) <= fColorType); SkASSERT(pos < fColors.count());
return &fColors[pos]; return &fColors[pos];
} }
@ -358,8 +378,8 @@ protected:
The function decides whether stop values should be used or not. The return value indicates The function decides whether stop values should be used or not. The return value indicates
the number of colors, which will be capped by kMaxRandomGradientColors. colors should be the number of colors, which will be capped by kMaxRandomGradientColors. colors should be
sized to be at least kMaxRandomGradientColors. stops is a pointer to an array of at least sized to be at least kMaxRandomGradientColors. stops is a pointer to an array of at least
size kMaxRandomGradientColors. It may be updated to nullptr, indicating that nullptr should be size kMaxRandomGradientColors. It may be updated to nullptr, indicating that nullptr should
passed to the gradient factory rather than the array. be passed to the gradient factory rather than the array.
*/ */
static const int kMaxRandomGradientColors = 4; static const int kMaxRandomGradientColors = 4;
static int RandomGradientParams(SkRandom* r, static int RandomGradientParams(SkRandom* r,
@ -376,26 +396,31 @@ protected:
private: private:
static const GrCoordSet kCoordSet = kLocal_GrCoordSet; static const GrCoordSet kCoordSet = kLocal_GrCoordSet;
SkTDArray<SkColor> fColors;
SkTDArray<SkScalar> fPositions;
SkShader::TileMode fTileMode;
GrCoordTransform fCoordTransform; GrCoordTransform fCoordTransform;
GrTextureAccess fTextureAccess; GrTextureAccess fTextureAccess;
SkScalar fYCoord; SkScalar fYCoord;
GrTextureStripAtlas* fAtlas; GrTextureStripAtlas* fAtlas;
int fRow; int fRow;
bool fIsOpaque; bool fIsOpaque;
SkGradientShaderBase::GpuColorType fColorType; ColorType fColorType;
SkColor fColors[3]; // More than 3 colors we use texture PremulType fPremulType; // This is already baked into the table for texture gradients, and
PremulType fPremulType; // This only changes behavior for two and three color special cases. // only changes behavior for gradients that don't use a texture.
// It is already baked into to the table for texture gradients.
typedef GrFragmentProcessor INHERITED; typedef GrFragmentProcessor INHERITED;
}; };
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Base class for GLSL gradient effects // Base class for GL gradient effects
class GrGradientEffect::GLSLProcessor : public GrGLSLFragmentProcessor { class GrGradientEffect::GLSLProcessor : public GrGLSLFragmentProcessor {
public: public:
GLSLProcessor(); GLSLProcessor() {
fCachedYCoord = SK_ScalarMax;
}
protected: protected:
void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override; void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
@ -412,10 +437,10 @@ protected:
// should call this method from their emitCode(). // should call this method from their emitCode().
void emitUniforms(GrGLSLUniformHandler*, const GrGradientEffect&); void emitUniforms(GrGLSLUniformHandler*, const GrGradientEffect&);
// Emit code that gets a fragment's color from an expression for t; has branches for
// emit code that gets a fragment's color from an expression for t; Has branches for 3 separate // several control flows inside -- 2-color gradients, 3-color symmetric gradients, 4+
// control flows inside -- 2 color gradients, 3 color symmetric gradients (both using // color gradients that use the traditional texture lookup, as well as several varieties
// native GLSL mix), and 4+ color gradients that use the traditional texture lookup. // of hard stop gradients
void emitColor(GrGLSLFPFragmentBuilder* fragBuilder, void emitColor(GrGLSLFPFragmentBuilder* fragBuilder,
GrGLSLUniformHandler* uniformHandler, GrGLSLUniformHandler* uniformHandler,
const GrGLSLCaps* caps, const GrGLSLCaps* caps,
@ -428,18 +453,30 @@ protected:
private: private:
enum { enum {
// First bit for premul before/after interp // First bit for premul before/after interp
kPremulBeforeInterpKey = 1, kPremulBeforeInterpKey = 1,
// Next two bits for 2/3 color type (neither means using texture atlas) // Next three bits for 2/3 color type or different special
kTwoColorKey = 4, // hard stop cases (neither means using texture atlas)
kThreeColorKey = 6, kTwoColorKey = 2,
kThreeColorKey = 4,
#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS
kHardStopCenteredKey = 6,
kHardStopZeroZeroOneKey = 8,
kHardStopZeroOneOneKey = 10,
// Next two bits for tile mode
kClampTileMode = 16,
kRepeatTileMode = 32,
kMirrorTileMode = 48,
// Lower six bits for premul, 2/3 color type, and tile mode
kReservedBits = 6,
#endif
}; };
SkScalar fCachedYCoord; SkScalar fCachedYCoord;
GrGLSLProgramDataManager::UniformHandle fColorsUni;
GrGLSLProgramDataManager::UniformHandle fFSYUni; GrGLSLProgramDataManager::UniformHandle fFSYUni;
GrGLSLProgramDataManager::UniformHandle fColorStartUni;
GrGLSLProgramDataManager::UniformHandle fColorMidUni;
GrGLSLProgramDataManager::UniformHandle fColorEndUni;
typedef GrGLSLFragmentProcessor INHERITED; typedef GrGLSLFragmentProcessor INHERITED;
}; };

View File

@ -218,10 +218,10 @@ void GrSweepGradient::GLSLSweepProcessor::emitCode(EmitArgs& args) {
// On Intel GPU there is an issue where it reads the second arguement to atan "- %s.x" as an int // On Intel GPU there is an issue where it reads the second arguement to atan "- %s.x" as an int
// thus must us -1.0 * %s.x to work correctly // thus must us -1.0 * %s.x to work correctly
if (args.fGLSLCaps->mustForceNegatedAtanParamToFloat()){ if (args.fGLSLCaps->mustForceNegatedAtanParamToFloat()){
t.printf("atan(- %s.y, -1.0 * %s.x) * 0.1591549430918 + 0.5", t.printf("(atan(- %s.y, -1.0 * %s.x) * 0.1591549430918 + 0.5)",
coords2D.c_str(), coords2D.c_str()); coords2D.c_str(), coords2D.c_str());
} else { } else {
t.printf("atan(- %s.y, - %s.x) * 0.1591549430918 + 0.5", t.printf("(atan(- %s.y, - %s.x) * 0.1591549430918 + 0.5)",
coords2D.c_str(), coords2D.c_str()); coords2D.c_str(), coords2D.c_str());
} }
this->emitColor(args.fFragBuilder, this->emitColor(args.fFragBuilder,