mirror of
https://github.com/mozilla/pdf.js.git
synced 2025-04-22 16:18:08 +02:00
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:
parent
d97c5a2b14
commit
6f65fef64b
4 changed files with 230 additions and 43 deletions
111
src/pattern.js
111
src/pattern.js
|
@ -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]);
|
||||
ctx.setTransform.apply(ctx, curMatrix);
|
||||
ctx.transform.apply(ctx, matrix);
|
||||
this.scaleToContext();
|
||||
|
||||
return ctx.createPattern(this.canvas, 'repeat');
|
||||
}
|
||||
|
|
24
src/util.js
24
src/util.js
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue