Fix star artifact in SkRRectsGaussianEdgeShader

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

Change-Id: I791ae5f44a88ac3e4debdf1d4092c605acdf4969
Reviewed-on: https://skia-review.googlesource.com/3360
Reviewed-by: Jim Van Verth <jvanverth@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
This commit is contained in:
Robert Phillips 2016-10-18 12:58:29 -04:00 committed by Skia Commit-Bot
parent 16adfa3933
commit 40085e62ba
2 changed files with 100 additions and 38 deletions

View File

@ -19,7 +19,7 @@ constexpr int kNumCols = 2;
constexpr int kNumRows = 5;
constexpr int kCellSize = 128;
constexpr SkScalar kPad = 8.0f;
constexpr SkScalar kBlurRadius = 8.0f;
constexpr SkScalar kInitialBlurRadius = 8.0f;
constexpr SkScalar kPeriod = 8.0f;
constexpr int kClipOffset = 32;
@ -292,7 +292,11 @@ public:
static const int kModeCount = kLast_Mode + 1;
RevealGM() : fFraction(0.5f), fMode(kRRectsGaussianEdge_Mode), fPause(false) {
RevealGM()
: fFraction(0.5f)
, fMode(kRRectsGaussianEdge_Mode)
, fPause(false)
, fBlurRadius(kInitialBlurRadius) {
this->setBGColor(sk_tool_utils::color_to_565(0xFFCCCCCC));
}
@ -349,14 +353,15 @@ protected:
SkPaint paint;
paint.setAntiAlias(true);
// G channel is an F6.2 radius
paint.setColor(SkColorSetARGB(255, 0, (unsigned char)(4*kBlurRadius), 0));
int iBlurRad = (int)(4.0f * fBlurRadius);
paint.setColor(SkColorSetARGB(255, iBlurRad >> 8, iBlurRad & 0xFF, 0));
paint.setShader(SkGaussianEdgeShader::Make());
drawObj->draw(canvas, paint);
canvas->restore();
} else if (kBlurMask_Mode == fMode) {
SkPath clippedPath;
SkScalar sigma = kBlurRadius / 4.0f;
SkScalar sigma = fBlurRadius / 4.0f;
if (clipObj->contains(drawObj->bounds())) {
clippedPath = drawObj->asPath(2.0f*sigma);
@ -383,7 +388,7 @@ protected:
if (clipObj->asRRect(&clipRR) && drawObj->asRRect(&drawnRR)) {
paint.setShader(SkRRectsGaussianEdgeShader::Make(clipRR, drawnRR,
kBlurRadius));
fBlurRadius));
}
canvas->drawRect(cover, paint);
@ -408,6 +413,12 @@ protected:
case 'C':
fMode = (Mode)((fMode + 1) % kModeCount);
return true;
case '+':
fBlurRadius += 1.0f;
return true;
case '-':
fBlurRadius = SkTMax(1.0f, fBlurRadius - 1.0f);
return true;
case 'p':
fPause = !fPause;
return true;
@ -427,6 +438,7 @@ private:
SkScalar fFraction;
Mode fMode;
bool fPause;
float fBlurRadius;
typedef GM INHERITED;
};

View File

@ -111,8 +111,9 @@ public:
const char* outputName,
const char indices[2]) { // how to access the params for the 2 rrects
// positive distance is towards the center of the circle
fragBuilder->codeAppendf("vec2 delta = %s.xy - %s.%s;",
// Positive distance is towards the center of the circle.
// Map all the cases to the lower right quadrant.
fragBuilder->codeAppendf("vec2 delta = abs(%s.xy - %s.%s);",
fragBuilder->fragmentPosition(), posName, indices);
switch (mode) {
@ -126,44 +127,95 @@ public:
break;
case kRect_Mode:
fragBuilder->codeAppendf(
"vec2 rectDist = vec2(1.0 - clamp((%s.%c - abs(delta.x))/%s, 0.0, 1.0),"
"1.0 - clamp((%s.%c - abs(delta.y))/%s, 0.0, 1.0));",
"vec2 rectDist = vec2(1.0 - clamp((%s.%c - delta.x)/%s, 0.0, 1.0),"
"1.0 - clamp((%s.%c - delta.y)/%s, 0.0, 1.0));",
sizesName, indices[0], radName,
sizesName, indices[1], radName);
fragBuilder->codeAppendf("%s = clamp(1.0 - length(rectDist), 0.0, 1.0);",
outputName);
break;
case kSimpleCircular_Mode:
// For the circular round rect we first compute the distance
// to the rect. Then we compute a multiplier that is 1 if the
// point is in one of the circular corners. We then compute the
// distance from the corner and then use the multiplier to mask
// between the two distances.
fragBuilder->codeAppendf("float xDist = clamp((%s.%c - abs(delta.x))/%s,"
"0.0, 1.0);",
// For the circular round rect we combine 2 distances:
// the fractional position from the corner inset point to the corner's circle
// the minimum perpendicular distance to the bounding rectangle
// The first distance is used when the pixel is inside the ice-cream-cone-shaped
// portion of a corner. The second is used everywhere else.
// This is intended to approximate the interpolation pattern if we had tessellated
// this geometry into a RRect outside and a rect inside.
//----------------
// rect distance computation
fragBuilder->codeAppendf("float xDist = (%s.%c - delta.x) / %s;",
sizesName, indices[0], radName);
fragBuilder->codeAppendf("float yDist = clamp((%s.%c - abs(delta.y))/%s,"
"0.0, 1.0);",
fragBuilder->codeAppendf("float yDist = (%s.%c - delta.y) / %s;",
sizesName, indices[1], radName);
fragBuilder->codeAppend("float rectDist = min(xDist, yDist);");
fragBuilder->codeAppend("float rectDist = clamp(min(xDist, yDist), 0.0, 1.0);");
fragBuilder->codeAppendf("vec2 cornerCenter = %s.%s - %s.%s;",
sizesName, indices, radiiName, indices);
fragBuilder->codeAppend("delta = vec2(abs(delta.x) - cornerCenter.x,"
"abs(delta.y) - cornerCenter.y);");
fragBuilder->codeAppendf("xDist = %s.%c - abs(delta.x);", radiiName, indices[0]);
fragBuilder->codeAppendf("yDist = %s.%c - abs(delta.y);", radiiName, indices[1]);
fragBuilder->codeAppend("float cornerDist = min(xDist, yDist);");
fragBuilder->codeAppend("float multiplier = step(0.0, cornerDist);");
//----------------
// ice-cream-cone fractional distance computation
fragBuilder->codeAppendf("delta += %s.%s;", radiiName, indices);
// When the blurRadius is larger than the corner radius we want to use it to
// compute the pointy end of the ice cream cone. If it smaller we just want to use
// the center of the corner's circle. When using the blurRadius the inset amount
// can't exceed the halfwidths of the RRect.
fragBuilder->codeAppendf("float insetDist = min(max(%s, %s.%c),min(%s.%c, %s.%c));",
radName, radiiName, indices[0],
sizesName, indices[0], sizesName, indices[1]);
// "maxValue" is a correction term for if the blurRadius is larger than the
// size of the RRect. In that case we don't want to go all the way to black.
fragBuilder->codeAppendf("float maxValue = insetDist/%s;", radName);
fragBuilder->codeAppendf("cornerDist = clamp((2.0 * %s.%c - length(delta))/%s,"
"0.0, 1.0);",
radiiName, indices[0], radName);
fragBuilder->codeAppendf("vec2 coneBottom = vec2(%s.%c - insetDist,"
"%s.%c - insetDist);",
sizesName, indices[0], sizesName, indices[1]);
fragBuilder->codeAppendf("%s = (multiplier * cornerDist) +"
"((1.0-multiplier) * rectDist);",
fragBuilder->codeAppendf("vec2 cornerTop = vec2(%s.%c - %s.%c, %s.%c) -"
"coneBottom;",
sizesName, indices[0], radiiName, indices[0],
sizesName, indices[1]);
fragBuilder->codeAppendf("vec2 cornerRight = vec2(%s.%c, %s.%c - %s.%c) -"
"coneBottom;",
sizesName, indices[0],
sizesName, indices[1], radiiName, indices[1]);
fragBuilder->codeAppend("vec2 ptInConeSpace = delta - coneBottom;");
fragBuilder->codeAppend("float distToPtInConeSpace = length(ptInConeSpace);");
fragBuilder->codeAppend("float cross1 = ptInConeSpace.x * cornerTop.y -"
"ptInConeSpace.y * cornerTop.x;");
fragBuilder->codeAppend("float cross2 = -ptInConeSpace.x * cornerRight.y + "
"ptInConeSpace.y * cornerRight.x;");
fragBuilder->codeAppend("float inCone = step(0.0, cross1) * step(0.0, cross2);");
fragBuilder->codeAppendf("vec2 cornerCenterInConeSpace = vec2(insetDist - %s.%c);",
radiiName, indices[0]);
fragBuilder->codeAppend("vec2 connectingVec = ptInConeSpace -"
"cornerCenterInConeSpace;");
fragBuilder->codeAppend("ptInConeSpace = normalize(ptInConeSpace);");
// "a" (i.e., dot(ptInConeSpace, ptInConeSpace) should always be 1.0f since
// ptInConeSpace is now normalized
fragBuilder->codeAppend("float b = 2.0 * dot(ptInConeSpace, connectingVec);");
fragBuilder->codeAppendf("float c = dot(connectingVec, connectingVec) - "
"%s.%c * %s.%c;",
radiiName, indices[0], radiiName, indices[0]);
fragBuilder->codeAppend("float fourAC = 4*c;");
// This max prevents sqrt(-1) when outside the cone
fragBuilder->codeAppend("float bSq = max(b*b, fourAC);");
// lop off negative values that are outside the cone
fragBuilder->codeAppend("float coneDist = max(0.0, "
"0.5 * (-b + sqrt(bSq - fourAC)));");
// make the coneDist a fraction of how far it is from the edge to the cone's base
fragBuilder->codeAppend("coneDist = (maxValue*coneDist) /"
"(coneDist+distToPtInConeSpace);");
fragBuilder->codeAppend("coneDist = clamp(coneDist, 0.0, 1.0);");
//----------------
fragBuilder->codeAppendf("%s = (inCone * coneDist) + ((1.0-inCone) * rectDist);",
outputName);
break;
}
@ -247,10 +299,8 @@ public:
// This is a bit of overkill since fX should equal fY for both round rects but it
// makes the shader code simpler.
pdman.set4f(fRadiiUni,
0.5f * first.getSimpleRadii().fX,
0.5f * first.getSimpleRadii().fY,
0.5f * second.getSimpleRadii().fX,
0.5f * second.getSimpleRadii().fY);
first.getSimpleRadii().fX, first.getSimpleRadii().fY,
second.getSimpleRadii().fX, second.getSimpleRadii().fY);
}
pdman.set1f(fRadiusUni, edgeFP.radius());
@ -264,7 +314,7 @@ public:
// For circles we still upload both width & height to simplify things
GrGLSLProgramDataManager::UniformHandle fSizesUni;
// The half corner radii of the two round rects (rx1/2, ry1/2, rx2/2, ry2/2)
// The corner radii of the two round rects (rx1, ry1, rx2, ry2)
// We upload both the x&y radii (although they are currently always the same) to make
// the indexing in the shader code simpler. In some future world we could also support
// non-circular corner round rects & ellipses.