This CL's base is the CL that sets up the distance vector field req. exposure: https://codereview.chromium.org/2114993002/

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

Review-Url: https://codereview.chromium.org/2151653002
This commit is contained in:
dvonbeck 2016-08-04 12:27:26 -07:00 committed by Commit bot
parent b8d1aac87a
commit 1b9e2fb494

View File

@ -19,12 +19,20 @@
#include "glsl/GrGLSLFragmentShaderBuilder.h"
#include "SkGr.h"
/** \class NormalBevelFP
*
* Fragment processor for the SkNormalBevelSource.
*
* @param bevelType type of the bevel
* @param bevelWidth width of the bevel in device space
* @param bevelHeight height of the bevel in device space
*/
class NormalBevelFP : public GrFragmentProcessor {
public:
NormalBevelFP(SkNormalSource::BevelType type, SkScalar width, SkScalar height)
: fType(type)
, fWidth(width)
, fHeight(height) {
NormalBevelFP(SkNormalSource::BevelType bevelType, SkScalar bevelWidth, SkScalar bevelHeight)
: fBevelType(bevelType)
, fBevelWidth(bevelWidth)
, fBevelHeight(bevelHeight) {
this->initClassID<NormalBevelFP>();
fUsesDistanceVectorField = true;
@ -39,23 +47,68 @@ public:
void onEmitCode(EmitArgs& args) override {
GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
const NormalBevelFP& fp = args.fFp.cast<NormalBevelFP>();
GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
const char* widthUniName = nullptr;
fWidthUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kFloat_GrSLType,
kDefault_GrSLPrecision, "Width", &widthUniName);
// Determining necessary uniforms and initializing them
bool needWidth = true;
bool needHeight = (fp.fBevelType == SkNormalSource::BevelType::kRoundedOut ||
fp.fBevelType == SkNormalSource::BevelType::kRoundedIn);
bool needNormalized = (fp.fBevelType == SkNormalSource::BevelType::kLinear);
const char *widthUniName = nullptr;
if (needWidth) {
fWidthUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kFloat_GrSLType,
kDefault_GrSLPrecision, "Width",
&widthUniName);
}
const char* heightUniName = nullptr;
fHeightUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kFloat_GrSLType,
kDefault_GrSLPrecision, "Height", &heightUniName);
if (needHeight) {
fHeightUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kFloat_GrSLType,
kDefault_GrSLPrecision, "Height",
&heightUniName);
}
fragBuilder->codeAppendf("%s = vec4(0.0, 0.0, 1.0, 0.0);", args.fOutputColor);
const char* normalizedWidthUniName = nullptr;
const char* normalizedHeightUniName = nullptr;
if (needNormalized) {
fNormalizedWidthUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
kFloat_GrSLType,
kDefault_GrSLPrecision,
"NormalizedWidth",
&normalizedWidthUniName);
fNormalizedHeightUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
kFloat_GrSLType,
kDefault_GrSLPrecision,
"NormalizedHeight",
&normalizedHeightUniName);
}
// Here we are splitting the distance vector into length and normalized direction
// TODO: Output these values from the geometry processor frag code instead of the vector
fragBuilder->codeAppendf("float dv_length = length(%s);",
fragBuilder->distanceVectorName());
fragBuilder->codeAppendf("vec2 dv_norm = normalize(%s);",
fragBuilder->distanceVectorName());
// Asserting presence of necessary uniforms
SkASSERT(widthUniName);
fragBuilder->codeAppend( "vec3 normal;");
fragBuilder->codeAppendf("if (dv_length >= %s) {", widthUniName);
fragBuilder->codeAppend( " normal = vec3(0.0, 0.0, 1.0);");
fragBuilder->codeAppend( "} else {");
this->emitMath(fragBuilder, fp.fBevelType, widthUniName, heightUniName,
normalizedWidthUniName, normalizedHeightUniName);
fragBuilder->codeAppend( "}");
fragBuilder->codeAppendf("%s = vec4(normal, 0.0);", args.fOutputColor);
}
static void GenKey(const GrProcessor& proc, const GrGLSLCaps&,
GrProcessorKeyBuilder* b) {
const NormalBevelFP& fp = proc.cast<NormalBevelFP>();
b->add32(static_cast<int>(fp.fType));
b->add32(static_cast<int>(fp.fBevelType));
}
protected:
@ -63,13 +116,92 @@ public:
const GrProcessor& proc) override {
const NormalBevelFP& normalBevelFP = proc.cast<NormalBevelFP>();
if (fPrevWidth != normalBevelFP.fWidth) {
pdman.set1f(fWidthUni, normalBevelFP.fWidth);
fPrevWidth = normalBevelFP.fWidth;
// Updating uniform if bevel type requires it and data has changed
bool needWidth = true;
bool needHeight = (normalBevelFP.fBevelType == SkNormalSource::BevelType::kRoundedOut ||
normalBevelFP.fBevelType == SkNormalSource::BevelType::kRoundedIn);
bool needNormalized = (normalBevelFP.fBevelType == SkNormalSource::BevelType::kLinear);
bool dirtyWidth = (fPrevWidth != normalBevelFP.fBevelWidth);
bool dirtyHeight = (fPrevHeight != normalBevelFP.fBevelHeight);
bool dirtyNormalized = (dirtyHeight || dirtyWidth);
if (needWidth && dirtyWidth) {
pdman.set1f(fWidthUni, normalBevelFP.fBevelWidth);
fPrevWidth = normalBevelFP.fBevelWidth;
}
if (fPrevHeight != normalBevelFP.fHeight) {
pdman.set1f(fHeightUni, normalBevelFP.fHeight);
fPrevHeight = normalBevelFP.fHeight;
if (needHeight && dirtyHeight) {
pdman.set1f(fHeightUni, normalBevelFP.fBevelHeight);
fPrevHeight = normalBevelFP.fBevelHeight;
}
if (needNormalized && dirtyNormalized) {
SkScalar height = normalBevelFP.fBevelHeight;
SkScalar width = normalBevelFP.fBevelWidth;
SkScalar length = SkScalarSqrt(SkScalarSquare(height) + SkScalarSquare(width));
pdman.set1f(fNormalizedHeightUni, height/length);
pdman.set1f(fNormalizedWidthUni, width/length);
}
}
// This method emits the code that calculates the normal orthgonal to the simulated beveled
// surface. In the comments inside the function, the math involved is described. For this
// purpose, the d-axis is defined to be the axis co-linear to the distance vector, where the
// origin is the end of the bevel inside the shape.
void emitMath(GrGLSLFPFragmentBuilder* fb, SkNormalSource::BevelType type,
const char* width, const char* height, const char* normalizedWidth,
const char* normalizedHeight) {
switch (type) {
case SkNormalSource::BevelType::kLinear:
// Asserting presence of necessary uniforms
SkASSERT(normalizedHeight);
SkASSERT(normalizedWidth);
// Because the slope of the bevel is -height/width, the vector
// normalized(vec2(height, width)) is the d- and z-components of the normal
// vector that is orthogonal to the linear bevel. Multiplying the d-component
// to the normalized distance vector splits it into x- and y-components.
fb->codeAppendf("normal = vec3(%s * dv_norm, %s);",
normalizedHeight, normalizedWidth);
break;
case SkNormalSource::BevelType::kRoundedOut:
// Fall through
case SkNormalSource::BevelType::kRoundedIn:
// Asserting presence of necessary uniforms
SkASSERT(height);
SkASSERT(width);
// Setting the current position in the d-axis to the distance from the end of
// the bevel as opposed to the beginning if the bevel is rounded in, essentially
// flipping the bevel calculations.
if ( type == SkNormalSource::BevelType::kRoundedIn ) {
fb->codeAppendf("float currentPos_d = %s - dv_length;", width);
} else if (type == SkNormalSource::BevelType::kRoundedOut) {
fb->codeAppendf("float currentPos_d = dv_length;");
}
fb->codeAppendf("float rootDOverW = sqrt(currentPos_d/%s);", width);
// Calculating the d- and z-components of the normal, where 'd' is the axis
// co-linear to the distance vector. Equation was derived from the formula for
// a bezier curve by solving the parametric equation for d(t) and z(t), then
// with those, calculate d'(t), z'(t) and t(d), and from these, d'(d) and z'(d).
// z'(d)/d'(d) results in the slope of the bevel at d, so we construct an
// orthogonal vector of slope -d'(d)/z'(d) and length 1.
fb->codeAppendf("vec2 unnormalizedNormal_dz = vec2(%s*(1.0-rootDOverW), "
"%s*rootDOverW);",
height, width);
fb->codeAppendf("vec2 normal_dz = normalize(unnormalizedNormal_dz);");
// Multiplying the d-component to the normalized distance vector splits it into
// x- and y-components.
fb->codeAppendf("normal = vec3(normal_dz.x*dv_norm, normal_dz.y);");
break;
default:
SkDEBUGFAIL("Invalid bevel type passed to emitMath");
}
}
@ -79,6 +211,11 @@ public:
SkScalar fPrevHeight;
GrGLSLProgramDataManager::UniformHandle fHeightUni;
// width / length(<width,height>)
GrGLSLProgramDataManager::UniformHandle fNormalizedWidthUni;
// height / length(<width,height>)
GrGLSLProgramDataManager::UniformHandle fNormalizedHeightUni;
};
void onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
@ -96,20 +233,23 @@ private:
bool onIsEqual(const GrFragmentProcessor& proc) const override {
const NormalBevelFP& normalBevelFP = proc.cast<NormalBevelFP>();
return fType == normalBevelFP.fType &&
fWidth == normalBevelFP.fWidth &&
fHeight == normalBevelFP.fHeight;
return fBevelType == normalBevelFP.fBevelType &&
fBevelWidth == normalBevelFP.fBevelWidth &&
fBevelHeight == normalBevelFP.fBevelHeight;
}
SkNormalSource::BevelType fType;
SkScalar fWidth;
SkScalar fHeight;
SkNormalSource::BevelType fBevelType;
SkScalar fBevelWidth;
SkScalar fBevelHeight;
};
sk_sp<GrFragmentProcessor> SkNormalBevelSourceImpl::asFragmentProcessor(
const SkShader::AsFPArgs&) const {
const SkShader::AsFPArgs& args) const {
return sk_make_sp<NormalBevelFP>(fType, fWidth, fHeight);
SkScalar maxScale = args.fViewMatrix->getMaxScale();
// Providing device-space width and height
return sk_make_sp<NormalBevelFP>(fType, maxScale * fWidth, maxScale * fHeight);
}
#endif // SK_SUPPORT_GPU