PDF: support perspective in simple shaders. (this version does not work well with tilling)
Review URL: https://codereview.chromium.org/26389006 git-svn-id: http://skia.googlecode.com/svn/trunk@11864 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
parent
7938bae14a
commit
eb5992c6cc
@ -648,16 +648,8 @@ private:
|
||||
void init(const SkClipStack* clipStack, const SkRegion& clipRegion,
|
||||
const SkMatrix& matrix, const SkPaint& paint, bool hasText) {
|
||||
fDstFormXObject = NULL;
|
||||
if (matrix.hasPerspective() ||
|
||||
(paint.getShader() &&
|
||||
paint.getShader()->getLocalMatrix().hasPerspective())) {
|
||||
// Just report that PDF does not supports perspective
|
||||
// TODO(edisonn): update the shape when possible
|
||||
// or dump in an image otherwise
|
||||
NOT_IMPLEMENTED(true, false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Shape has to be flatten before we get here.
|
||||
NOT_IMPLEMENTED(!matrix.hasPerspective(), false);
|
||||
if (paint.getXfermode()) {
|
||||
paint.getXfermode()->asMode(&fXfermode);
|
||||
}
|
||||
@ -705,9 +697,8 @@ SkPDFDevice::SkPDFDevice(const SkISize& pageSize, const SkISize& contentSize,
|
||||
fLastMarginContentEntry(NULL),
|
||||
fClipStack(NULL),
|
||||
fEncoder(NULL) {
|
||||
// just report that PDF does not supports perspective
|
||||
// TODO(edisonn): update the shape when possible
|
||||
// or dump in an image otherwise
|
||||
// Just report that PDF does not supports perspective in the
|
||||
// initial transform.
|
||||
NOT_IMPLEMENTED(initialTransform.hasPerspective(), true);
|
||||
|
||||
// Skia generally uses the top left as the origin but PDF natively has the
|
||||
|
@ -210,16 +210,74 @@ static void tileModeCode(SkShader::TileMode mode, SkString* result) {
|
||||
}
|
||||
}
|
||||
|
||||
static SkString linearCode(const SkShader::GradientInfo& info) {
|
||||
SkString function("{pop\n"); // Just ditch the y value.
|
||||
/**
|
||||
* Returns PS function code that would apply perspective to a x, y point.
|
||||
* The function assumes that the stack has at least two elements,
|
||||
* and that the top 2 elements are numeric values.
|
||||
* After ececuting this code on a PS stack, the last 2 elements are updated
|
||||
* while the rest of the stack is preserved intact.
|
||||
* xy2xy is the inverse perspective matrix.
|
||||
*/
|
||||
static SkString apply_perspective_to_coordinates(
|
||||
const SkMatrix& inversePerspectiveMatrix) {
|
||||
SkString code;
|
||||
if (!inversePerspectiveMatrix.hasPerspective()) {
|
||||
return code;
|
||||
}
|
||||
|
||||
// Perspective matrix should be:
|
||||
// 1 0 0
|
||||
// 0 1 0
|
||||
// p0 p1 p2
|
||||
|
||||
SkScalar p0 = inversePerspectiveMatrix[SkMatrix::kMPersp0];
|
||||
SkScalar p1 = inversePerspectiveMatrix[SkMatrix::kMPersp1];
|
||||
SkScalar p2 = inversePerspectiveMatrix[SkMatrix::kMPersp2];
|
||||
|
||||
// y = y / (p2 + p0 x + p1 y)
|
||||
// x = x / (p2 + p0 x + p1 y)
|
||||
|
||||
// Input on stack: x y
|
||||
code.append("dup "); // x y y
|
||||
code.appendScalar(p1); // x y y p1
|
||||
code.append(" mul " // x y y*p1
|
||||
" 2 index "); // x y y*p1 x
|
||||
code.appendScalar(p0); // x y y p1 x p0
|
||||
code.append(" mul "); // x y y*p1 x*p0
|
||||
code.appendScalar(p2); // x y y p1 x*p0 p2
|
||||
code.append("add " // x y y*p1 x*p0+p2
|
||||
"add " // x y y*p1+x*p0+p2
|
||||
"1 index " // x y y*p1+x*p0+p2 y
|
||||
"1 index " // x y y*p1+x*p0+p2 y y*p1+x*p0+p2
|
||||
"div " // x y y*p1+x*p0+p2 y/(y*p1+x*p0+p2)
|
||||
"4 1 roll " // y/(y*p1+x*p0+p2) x y y*p1+x*p0+p2
|
||||
"exch " // y/(y*p1+x*p0+p2) x y*p1+x*p0+p2 y
|
||||
"pop " // y/(y*p1+x*p0+p2) x y*p1+x*p0+p2
|
||||
"div " // y/(y*p1+x*p0+p2) x/(y*p1+x*p0+p2)
|
||||
"exch\n"); // x/(y*p1+x*p0+p2) y/(y*p1+x*p0+p2)
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
static SkString linearCode(const SkShader::GradientInfo& info,
|
||||
const SkMatrix& perspectiveRemover) {
|
||||
SkString function("{");
|
||||
|
||||
function.append(apply_perspective_to_coordinates(perspectiveRemover));
|
||||
|
||||
function.append("pop\n"); // Just ditch the y value.
|
||||
tileModeCode(info.fTileMode, &function);
|
||||
gradientFunctionCode(info, &function);
|
||||
function.append("}");
|
||||
return function;
|
||||
}
|
||||
|
||||
static SkString radialCode(const SkShader::GradientInfo& info) {
|
||||
static SkString radialCode(const SkShader::GradientInfo& info,
|
||||
const SkMatrix& perspectiveRemover) {
|
||||
SkString function("{");
|
||||
|
||||
function.append(apply_perspective_to_coordinates(perspectiveRemover));
|
||||
|
||||
// Find the distance from the origin.
|
||||
function.append("dup " // x y y
|
||||
"mul " // x y^2
|
||||
@ -239,7 +297,8 @@ static SkString radialCode(const SkShader::GradientInfo& info) {
|
||||
with one simplification, the coordinate space has been scaled so that
|
||||
Dr = 1. This means we don't need to scale the entire equation by 1/Dr^2.
|
||||
*/
|
||||
static SkString twoPointRadialCode(const SkShader::GradientInfo& info) {
|
||||
static SkString twoPointRadialCode(const SkShader::GradientInfo& info,
|
||||
const SkMatrix& perspectiveRemover) {
|
||||
SkScalar dx = info.fPoint[0].fX - info.fPoint[1].fX;
|
||||
SkScalar dy = info.fPoint[0].fY - info.fPoint[1].fY;
|
||||
SkScalar sr = info.fRadius[0];
|
||||
@ -249,6 +308,9 @@ static SkString twoPointRadialCode(const SkShader::GradientInfo& info) {
|
||||
// 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(apply_perspective_to_coordinates(perspectiveRemover));
|
||||
|
||||
function.append("2 copy ");
|
||||
|
||||
// Calculate -b and b^2.
|
||||
@ -286,7 +348,8 @@ static SkString twoPointRadialCode(const SkShader::GradientInfo& info) {
|
||||
/* 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) {
|
||||
static SkString twoPointConicalCode(const SkShader::GradientInfo& info,
|
||||
const SkMatrix& perspectiveRemover) {
|
||||
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];
|
||||
@ -300,6 +363,9 @@ static SkString twoPointConicalCode(const SkShader::GradientInfo& info) {
|
||||
// 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(apply_perspective_to_coordinates(perspectiveRemover));
|
||||
|
||||
function.append("2 copy ");
|
||||
|
||||
// Calculate b and b^2; b = -2 * (y * dy + x * dx + r0 * dr).
|
||||
@ -395,7 +461,8 @@ static SkString twoPointConicalCode(const SkShader::GradientInfo& info) {
|
||||
return function;
|
||||
}
|
||||
|
||||
static SkString sweepCode(const SkShader::GradientInfo& info) {
|
||||
static SkString sweepCode(const SkShader::GradientInfo& info,
|
||||
const SkMatrix& perspectiveRemover) {
|
||||
SkString function("{exch atan 360 div\n");
|
||||
tileModeCode(info.fTileMode, &function);
|
||||
gradientFunctionCode(info, &function);
|
||||
@ -725,10 +792,49 @@ SkPDFAlphaFunctionShader::SkPDFAlphaFunctionShader(SkPDFShader::State* state)
|
||||
SkMatrix::I());
|
||||
}
|
||||
|
||||
// Finds affine and persp such that in = affine * persp.
|
||||
// but it returns the inverse of perspective matrix.
|
||||
static bool split_perspective(const SkMatrix in, SkMatrix* affine,
|
||||
SkMatrix* perspectiveInverse) {
|
||||
const SkScalar p2 = in[SkMatrix::kMPersp2];
|
||||
|
||||
if (SkScalarNearlyZero(p2)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const SkScalar zero = SkIntToScalar(0);
|
||||
const SkScalar one = SkIntToScalar(1);
|
||||
|
||||
const SkScalar sx = in[SkMatrix::kMScaleX];
|
||||
const SkScalar kx = in[SkMatrix::kMSkewX];
|
||||
const SkScalar tx = in[SkMatrix::kMTransX];
|
||||
const SkScalar ky = in[SkMatrix::kMSkewY];
|
||||
const SkScalar sy = in[SkMatrix::kMScaleY];
|
||||
const SkScalar ty = in[SkMatrix::kMTransY];
|
||||
const SkScalar p0 = in[SkMatrix::kMPersp0];
|
||||
const SkScalar p1 = in[SkMatrix::kMPersp1];
|
||||
|
||||
// Perspective matrix would be:
|
||||
// 1 0 0
|
||||
// 0 1 0
|
||||
// p0 p1 p2
|
||||
// But we need the inverse of persp.
|
||||
perspectiveInverse->setAll(one, zero, zero,
|
||||
zero, one, zero,
|
||||
-p0 / p2, -p1/p2, 1/p2);
|
||||
|
||||
affine->setAll(sx - p0 * tx / p2, kx - p1 * tx / p2, tx / p2,
|
||||
ky - p0 * ty / p2, sy - p1 * ty / p2, ty / p2,
|
||||
zero, zero, one);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
SkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state)
|
||||
: SkPDFDict("Pattern"),
|
||||
fState(state) {
|
||||
SkString (*codeFunction)(const SkShader::GradientInfo& info) = NULL;
|
||||
SkString (*codeFunction)(const SkShader::GradientInfo& info,
|
||||
const SkMatrix& perspectiveRemover) = NULL;
|
||||
SkPoint transformPoints[2];
|
||||
|
||||
// Depending on the type of the gradient, we want to transform the
|
||||
@ -780,8 +886,24 @@ SkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state)
|
||||
// the gradient can be drawn on on the unit segment.
|
||||
SkMatrix mapperMatrix;
|
||||
unitToPointsMatrix(transformPoints, &mapperMatrix);
|
||||
|
||||
SkMatrix perspectiveInverseOnly = SkMatrix::I();
|
||||
|
||||
SkMatrix finalMatrix = fState.get()->fCanvasTransform;
|
||||
finalMatrix.preConcat(fState.get()->fShaderTransform);
|
||||
|
||||
// Preserves as much as posible in the final matrix, and only removes
|
||||
// the perspective. The inverse of the perspective is stored in
|
||||
// perspectiveInverseOnly matrix and has 3 useful numbers
|
||||
// (p0, p1, p2), while everything else is either 0 or 1.
|
||||
// In this way the shader will handle it eficiently, with minimal code.
|
||||
if (finalMatrix.hasPerspective()) {
|
||||
if (!split_perspective(finalMatrix,
|
||||
&finalMatrix, &perspectiveInverseOnly)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
finalMatrix.preConcat(mapperMatrix);
|
||||
|
||||
SkRect bbox;
|
||||
@ -812,9 +934,9 @@ SkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state)
|
||||
inverseMapperMatrix.mapRadius(info->fRadius[0]);
|
||||
twoPointRadialInfo.fRadius[1] =
|
||||
inverseMapperMatrix.mapRadius(info->fRadius[1]);
|
||||
functionCode = codeFunction(twoPointRadialInfo);
|
||||
functionCode = codeFunction(twoPointRadialInfo, perspectiveInverseOnly);
|
||||
} else {
|
||||
functionCode = codeFunction(*info);
|
||||
functionCode = codeFunction(*info, perspectiveInverseOnly);
|
||||
}
|
||||
|
||||
SkAutoTUnref<SkPDFDict> pdfShader(new SkPDFDict);
|
||||
|
Loading…
Reference in New Issue
Block a user