Added pdf implementation of conical gradient.

Also fixed an erroneous comment in the gpu implementation.
Review URL: https://codereview.appspot.com/6351076

git-svn-id: http://skia.googlecode.com/svn/trunk@4520 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
rileya@google.com 2012-07-10 20:05:23 +00:00
parent a4091ba864
commit 6219728eec
3 changed files with 119 additions and 5 deletions

View File

@ -62,9 +62,6 @@ protected:
SkISize onISize() { return skiagm::make_isize(480, 780); }
// BUG: PDF code (at least on mac) fails when we run this
virtual uint32_t onGetFlags() const SK_OVERRIDE { return kSkipPDF_Flag; }
virtual void onDraw(SkCanvas* canvas) {
if (false) {
SkPaint paint;

View File

@ -451,7 +451,7 @@ void GrGLConical2Gradient::emitFS(GrGLShaderBuilder* state,
SkString r1Name("r1");
SkString tName("t");
SkString p0; // 4a
SkString p1; // 1/(2a)
SkString p1; // 1/a
SkString p2; // distance between centers
SkString p3; // start radius
SkString p4; // start radius squared

View File

@ -274,6 +274,118 @@ static SkString twoPointRadialCode(const SkShader::GradientInfo& info) {
return function;
}
/* Conical gradient shader, based on the Canvas spec for radial gradients
See: http://www.w3.org/TR/2dcontext/#dom-context-2d-createradialgradient
*/
static SkString twoPointConicalCode(const SkShader::GradientInfo& info) {
SkScalar dx = info.fPoint[1].fX - info.fPoint[0].fX;
SkScalar dy = info.fPoint[1].fY - info.fPoint[0].fY;
SkScalar r0 = info.fRadius[0];
SkScalar dr = info.fRadius[1] - info.fRadius[0];
SkScalar a = SkScalarMul(dx, dx) + SkScalarMul(dy, dy) -
SkScalarMul(dr, dr);
// First compute t, if the pixel falls outside the cone, then we'll end
// with 'false' on the stack, otherwise we'll push 'true' with t below it
// We start with a stack of (x y), copy it and then consume one copy in
// order to calculate b and the other to calculate c.
SkString function("{");
function.append("2 copy ");
// Calculate b and b^2; b = -2 * (y * dy + x * dx + r0 * dr).
function.appendScalar(dy);
function.append(" mul exch ");
function.appendScalar(dx);
function.append(" mul add ");
function.appendScalar(SkScalarMul(r0, dr));
function.append(" add -2 mul dup dup mul\n");
// c = x^2 + y^2 + radius0^2
function.append("4 2 roll dup mul exch dup mul add ");
function.appendScalar(SkScalarMul(r0, r0));
function.append(" sub dup 4 1 roll\n");
// Contents of the stack at this point: c, b, b^2, c
// if a = 0, then we collapse to a simpler linear case
if (a == 0) {
// t = -c/b
function.append("pop pop div neg dup ");
// compute radius(t)
function.appendScalar(dr);
function.append(" mul ");
function.appendScalar(r0);
function.append(" add\n");
// if r(t) < 0, then it's outside the cone
function.append("0 lt {clear false} {true} ifelse\n");
} else {
// quadratic case: the Canvas spec wants the largest
// root t for which radius(t) > 0
// compute the discriminant (b^2 - 4ac)
function.appendScalar(SkScalarMul(SkIntToScalar(4), a));
function.append(" mul sub dup\n");
// if d >= 0, proceed
function.append("0 ge {\n");
// an intermediate value we'll use to compute the roots:
// q = -0.5 * (b +/- sqrt(d))
function.append("sqrt exch dup 0 lt {exch -1 mul} if");
function.append(" add -0.5 mul dup\n");
// first root = q / a
function.appendScalar(a);
function.append(" div\n");
// second root = c / q
function.append("3 1 roll div\n");
// put the larger root on top of the stack
function.append("2 copy gt {exch} if\n");
// compute radius(t) for larger root
function.append("dup ");
function.appendScalar(dr);
function.append(" mul ");
function.appendScalar(r0);
function.append(" add\n");
// if r(t) > 0, we have our t, pop off the smaller root and we're done
function.append(" 0 gt {exch pop true}\n");
// otherwise, throw out the larger one and try the smaller root
function.append("{pop dup\n");
function.appendScalar(dr);
function.append(" mul ");
function.appendScalar(r0);
function.append(" add\n");
// if r(t) < 0, push false, otherwise the smaller root is our t
function.append("0 le {clear false} {true} ifelse\n");
function.append("} ifelse\n");
// d < 0, clear the stack and push false
function.append("} {clear false} ifelse\n");
}
// if the pixel is in the cone, proceed to compute a color
function.append("{");
tileModeCode(info.fTileMode, &function);
gradientFunctionCode(info, &function);
// otherwise, just write black
function.append("} {0 0 0} ifelse }");
return function;
}
static SkString sweepCode(const SkShader::GradientInfo& info) {
SkString function("{exch atan 360 div\n");
tileModeCode(info.fTileMode, &function);
@ -467,6 +579,12 @@ SkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state)
codeFunction = &twoPointRadialCode;
break;
}
case SkShader::kConical_GradientType: {
transformPoints[1] = transformPoints[0];
transformPoints[1].fX += SK_Scalar1;
codeFunction = &twoPointConicalCode;
break;
}
case SkShader::kSweep_GradientType:
transformPoints[1] = transformPoints[0];
transformPoints[1].fX += SK_Scalar1;
@ -474,7 +592,6 @@ SkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state)
break;
case SkShader::kColor_GradientType:
case SkShader::kNone_GradientType:
case SkShader::kConical_GradientType: // similar to twoPointRadialCode?
default:
return;
}