Sharpen the patterns.

Draw the patterns with the intended resolution instead of scaling
afterwards. Scaling leads to unclear patterns.

Also:
Make TilingPattern function for paintType switch case.
Make TilingPattern function for bbox clipping.
Make TilingPattern functions for scaling code.
Increase MAX_PATTERN_SIZE to 4096.
Add Singular Value Decomposition function.
This commit is contained in:
Kalervo Kujala 2012-12-27 09:35:25 +02:00
parent d97c5a2b14
commit 6f65fef64b
4 changed files with 230 additions and 43 deletions

View File

@ -268,11 +268,11 @@ var TilingPattern = (function TilingPatternClosure() {
COLORED: 1,
UNCOLORED: 2
};
var MAX_PATTERN_SIZE = 512;
var MAX_PATTERN_SIZE = 4096;
function TilingPattern(IR, color, ctx, objs) {
var operatorList = IR[2];
this.matrix = IR[3];
this.matrix = IR[3] || [1, 0, 0, 1, 0, 0];
var bbox = IR[4];
var xstep = IR[5];
var ystep = IR[6];
@ -294,14 +294,21 @@ var TilingPattern = (function TilingPatternClosure() {
var width = botRight[0] - topLeft[0];
var height = botRight[1] - topLeft[1];
// TODO: hack to avoid OOM, we would ideally compute the tiling
// pattern to be only as large as the acual size in device space
// This could be computed with .mozCurrentTransform, but still
// needs to be implemented
while (Math.abs(width) > MAX_PATTERN_SIZE ||
Math.abs(height) > MAX_PATTERN_SIZE) {
width = height = MAX_PATTERN_SIZE;
}
// Obtain scale from matrix and current transformation matrix.
var matrixScale = Util.singularValueDecompose2dScale(this.matrix);
var curMatrixScale = Util.singularValueDecompose2dScale(this.curMatrix);
var combinedScale = [matrixScale[0] * curMatrixScale[0],
matrixScale[1] * curMatrixScale[1]];
// MAX_PATTERN_SIZE is used to avoid OOM situation.
// Use width and height values that are as close as possible to the end
// result when the pattern is used. Too low value makes the pattern look
// blurry. Too large value makes it look too crispy.
width = Math.min(Math.ceil(Math.abs(width * combinedScale[0])),
MAX_PATTERN_SIZE);
height = Math.min(Math.ceil(Math.abs(height * combinedScale[1])),
MAX_PATTERN_SIZE);
var tmpCanvas = createScratchCanvas(width, height);
@ -309,37 +316,16 @@ var TilingPattern = (function TilingPatternClosure() {
var tmpCtx = tmpCanvas.getContext('2d');
var graphics = new CanvasGraphics(tmpCtx, null, objs);
switch (paintType) {
case PaintType.COLORED:
tmpCtx.fillStyle = ctx.fillStyle;
tmpCtx.strokeStyle = ctx.strokeStyle;
break;
case PaintType.UNCOLORED:
var rgbColor = new DeviceRgbCS().getRgb(color, 0);
var cssColor = Util.makeCssRgb(rgbColor);
tmpCtx.fillStyle = cssColor;
tmpCtx.strokeStyle = cssColor;
break;
default:
error('Unsupported paint type: ' + paintType);
}
this.setFillAndStrokeStyleToContext(tmpCtx, paintType, color);
var scale = [width / xstep, height / ystep];
this.scale = scale;
this.setScale(width, height, xstep, ystep);
this.transformToScale(graphics);
// transform coordinates to pattern space
var tmpTranslate = [1, 0, 0, 1, -topLeft[0], -topLeft[1]];
var tmpScale = [scale[0], 0, 0, scale[1], 0, 0];
graphics.transform.apply(graphics, tmpScale);
graphics.transform.apply(graphics, tmpTranslate);
if (bbox && isArray(bbox) && 4 == bbox.length) {
var bboxWidth = x1 - x0;
var bboxHeight = y1 - y0;
graphics.rectangle(x0, y0, bboxWidth, bboxHeight);
graphics.clip();
graphics.endPath();
}
this.clipBbox(graphics, bbox, x0, y0, x1, y1);
graphics.executeOperatorList(operatorList);
@ -361,19 +347,58 @@ var TilingPattern = (function TilingPatternClosure() {
};
TilingPattern.prototype = {
setScale: function TilingPattern_setScale(width, height, xstep, ystep) {
this.scale = [width / xstep, height / ystep];
},
transformToScale: function TilingPattern_transformToScale(graphics) {
var scale = this.scale;
var tmpScale = [scale[0], 0, 0, scale[1], 0, 0];
graphics.transform.apply(graphics, tmpScale);
},
scaleToContext: function TilingPattern_scaleToContext() {
var scale = this.scale;
this.ctx.scale(1 / scale[0], 1 / scale[1]);
},
clipBbox: function clipBbox(graphics, bbox, x0, y0, x1, y1) {
if (bbox && isArray(bbox) && 4 == bbox.length) {
var bboxWidth = x1 - x0;
var bboxHeight = y1 - y0;
graphics.rectangle(x0, y0, bboxWidth, bboxHeight);
graphics.clip();
graphics.endPath();
}
},
setFillAndStrokeStyleToContext:
function setFillAndStrokeStyleToContext(context, paintType, color) {
switch (paintType) {
case PaintType.COLORED:
var ctx = this.ctx;
context.fillStyle = ctx.fillStyle;
context.strokeStyle = ctx.strokeStyle;
break;
case PaintType.UNCOLORED:
var rgbColor = new DeviceRgbCS().getRgb(color, 0);
var cssColor = Util.makeCssRgb(rgbColor);
context.fillStyle = cssColor;
context.strokeStyle = cssColor;
break;
default:
error('Unsupported paint type: ' + paintType);
}
},
getPattern: function TilingPattern_getPattern() {
var matrix = this.matrix;
var curMatrix = this.curMatrix;
var ctx = this.ctx;
if (curMatrix)
ctx.setTransform.apply(ctx, curMatrix);
if (matrix)
ctx.transform.apply(ctx, matrix);
var scale = this.scale;
ctx.scale(1 / scale[0], 1 / scale[1]);
this.scaleToContext();
return ctx.createPattern(this.canvas, 'repeat');
}

View File

@ -257,6 +257,30 @@ var Util = PDFJS.Util = (function UtilClosure() {
];
};
// This calculation uses Singular Value Decomposition.
// The SVD can be represented with formula A = USV. We are interested in the
// matrix S here because it represents the scale values.
Util.singularValueDecompose2dScale =
function Util_singularValueDecompose2dScale(m) {
var transpose = [m[0], m[2], m[1], m[3]];
// Multiply matrix m with its transpose.
var a = m[0] * transpose[0] + m[1] * transpose[2];
var b = m[0] * transpose[1] + m[1] * transpose[3];
var c = m[2] * transpose[0] + m[3] * transpose[2];
var d = m[2] * transpose[1] + m[3] * transpose[3];
// Solve the second degree polynomial to get roots.
var first = (a + d) / 2;
var second = Math.sqrt((a + d) * (a + d) - 4 * (a * d - c * b)) / 2;
var sx = first + second || 1;
var sy = first - second || 1;
// Scale values are the square roots of the eigenvalues.
return [Math.sqrt(sx), Math.sqrt(sy)];
};
// Normalize rectangle rect=[x1, y1, x2, y2] so that (x1,y1) < (x2,y2)
// For coordinate systems whose origin lies in the bottom-left, this
// means normalization to (BL,TR) ordering. For systems with origin in the

132
test/pdfs/issue2177.pdf Normal file
View File

@ -0,0 +1,132 @@
%PDF-1.7
1 0 obj % entry point
<<
/Type /Catalog
/Pages 2 0 R
>>
endobj
2 0 obj
<< /Type /Pages
/MediaBox [ 0 0 900 900 ]
/Count 1
/Kids [ 3 0 R ]
>>
endobj
3 0 obj % Page object
<< /Type /Page
/Parent 2 0 R
/Resources 4 0 R
/Contents 7 0 R
/CropBox [ 0 0 225 225 ]
>>
endobj
4 0 obj % Resource dictionary for page
<< /Pattern << /P1 5 0 R >>
>>
endobj
5 0 obj % Pattern definition
<< /Type /Pattern
/PatternType 1 % Tiling pattern
/PaintType 1 % Colored
/TilingType 2
/BBox [ 0 0 100 100 ]
/XStep 20
/YStep 20
/Resources 6 0 R
/Matrix [ 1.4 1.0 -0.5 0.7 0.0 0.0 ]
/Length 931
>>
stream
1.0 0.0 0.0 RG % Set stroking color to red
1 4.75 m % Construct lower-left circle
1 6.81919 2.68081 8.5 4.75 8.5 c
6.81919 8.5 8.5 6.81919 8.5 4.75 c
8.5 2.68081 6.81919 1 4.75 1 c
2.68081 1 1 2.68081 1 4.75 c
S
0.0 1.0 0.0 RG % Set stroking color to green
12.25 4.75 m % Construct lower-right circle
12.25 6.81919 13.93081 8.5 16 8.5 c
18.06919 8.5 19.75 6.81919 19.75 4.75 c
19.75 2.68081 18.06919 1 16 1 c
13.93081 1 12.25 2.68081 12.25 4.75 c
S
0.0 0.0 1.0 RG % Set stroking color to blue
1 16 m % Construct upper-left circle
1 18.06919 2.68081 19.75 4.75 19.75 c
6.81919 19.75 8.5 18.06919 8.5 16 c
8.5 13.93081 6.81919 12.25 4.75 12.25 c
2.68081 12.25 1 13.93081 1 16 c
S
0.0 0.0 0.0 RG % Set stroking color to black
12.25 16 m % Construct upper-right circle
12.25 18.06919 13.93081 19.75 16 19.75 c
18.06919 19.75 19.75 18.06919 19.75 16 c
19.75 13.93081 18.06919 12.25 16 12.25 c
13.93081 12.25 12.25 13.93081 12.25 16 c
s
endstream
endobj
6 0 obj % Resource dictionary for pattern
<<
>>
endobj
7 0 obj % Contents of page
<< /Length 1082 >>
stream
0.0 G % Set stroking color to black
1.0 1.0 0.0 rg % Set nonstroking color to yellow
25 175 175 -150 re % Construct rectangular path
f % Fill path
/Pattern cs % Set pattern color space
/P1 scn % Set pattern as nonstroking color
99.92 49.92 m % Start new path
99.92 77.52 77.52 99.92 49.92 99.92 c % Construct lower-left circle
22.32 99.92 -0.08 77.52 -0.08 49.92 c
-0.08 22.32 22.32 -0.08 49.92 -0.08 c
77.52 -0.08 99.92 22.32 99.92 49.92 c
B % Fill and stroke path
224.96 49.92 m % Start new path
224.96 77.52 202.56 99.92 174.96 99.92 c % Construct lower-right circle
147.36 99.92 124.96 77.52 124.96 49.92 c
124.96 22.32 147.36 -0.08 174.96 -0.08 c
202.56 -0.08 224.96 22.32 224.96 49.92 c
B % Fill and stroke path
87.56 201.70 m % Start new path
63.66 187.90 55.46 157.32 69.26 133.40 c % Construct upper circle
83.06 109.50 113.66 101.30 137.56 115.10 c
161.46 128.90 169.66 159.50 155.86 183.40 c
142.06 207.30 111.46 215.50 87.56 201.70 c
B % Fill and stroke path
50 50 m % Start new path
175 50 l % Construct triangular path
112.5 158.253 l
b % Close, fill, and stroke path
endstream
endobj
xref
0 8
0000000000 65535 f
0000000010 00000 n
0000000078 00000 n
0000000173 00000 n
0000000305 00000 n
0000000383 00000 n
0000001594 00000 n
0000001650 00000 n
trailer
<<
/Size 8
/Root 1 0 R
>>
startxref
2803
%%EOF

View File

@ -957,5 +957,11 @@
"link": true,
"rounds": 1,
"type": "eq"
},
{ "id": "issue2177-eq",
"file": "pdfs/issue2177.pdf",
"md5": "48a808278bf31de8414c4e03ecd0900a",
"rounds": 1,
"type": "eq"
}
]