SVG: optimize transform group creation
This patch ensures that we only create transformation groups when it is actually required and that we re-use transform groups as much as possible. It reduces the number of transform groups for the Tracemonkey paper from 2790 to 1271, thereby making the DOM much lighter and rendering/scrolling smoother. Moreover, it simplifies the code and prevents duplication. Finally, we issue a warning when an unimplemented graphic state is encountered. Before, this was ignored silently, making debugging harder.
This commit is contained in:
parent
de6c92a96d
commit
fa90573c4b
@ -409,9 +409,7 @@ var SVGGraphics = (function SVGGraphicsClosure() {
|
||||
this.transformMatrix = this.transformStack.pop();
|
||||
this.current = this.extraStack.pop();
|
||||
|
||||
this.tgrp = document.createElementNS(NS, 'svg:g');
|
||||
this.tgrp.setAttributeNS(null, 'transform', pm(this.transformMatrix));
|
||||
this.pgrp.appendChild(this.tgrp);
|
||||
this.tgrp = null;
|
||||
},
|
||||
|
||||
group: function SVGGraphics_group(items) {
|
||||
@ -453,9 +451,7 @@ var SVGGraphics = (function SVGGraphicsClosure() {
|
||||
var transformMatrix = [a, b, c, d, e, f];
|
||||
this.transformMatrix = Util.transform(this.transformMatrix,
|
||||
transformMatrix);
|
||||
|
||||
this.tgrp = document.createElementNS(NS, 'svg:g');
|
||||
this.tgrp.setAttributeNS(null, 'transform', pm(this.transformMatrix));
|
||||
this.tgrp = null;
|
||||
},
|
||||
|
||||
getSVG: function SVGGraphics_getSVG(operatorList, viewport) {
|
||||
@ -464,14 +460,9 @@ var SVGGraphics = (function SVGGraphicsClosure() {
|
||||
|
||||
return this.loadDependencies(operatorList).then(function () {
|
||||
this.transformMatrix = IDENTITY_MATRIX;
|
||||
this.pgrp = document.createElementNS(NS, 'svg:g'); // Parent group
|
||||
this.pgrp.setAttributeNS(null, 'transform', pm(viewport.transform));
|
||||
this.tgrp = document.createElementNS(NS, 'svg:g'); // Transform group
|
||||
this.tgrp.setAttributeNS(null, 'transform', pm(this.transformMatrix));
|
||||
this.defs = document.createElementNS(NS, 'svg:defs');
|
||||
this.pgrp.appendChild(this.defs);
|
||||
this.pgrp.appendChild(this.tgrp);
|
||||
this.svg.appendChild(this.pgrp);
|
||||
this.svg.setAttributeNS(null, 'transform', pm(viewport.transform));
|
||||
this.svg.appendChild(this.defs);
|
||||
var opTree = this.convertOpList(operatorList);
|
||||
this.executeOpTree(opTree);
|
||||
return this.svg;
|
||||
@ -633,7 +624,7 @@ var SVGGraphics = (function SVGGraphicsClosure() {
|
||||
this.group(opTree[x].items);
|
||||
break;
|
||||
default:
|
||||
warn('Unimplemented method '+ fn);
|
||||
warn('Unimplemented operator ' + fn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -759,8 +750,7 @@ var SVGGraphics = (function SVGGraphicsClosure() {
|
||||
current.txtElement.appendChild(current.tspan);
|
||||
current.txtgrp.appendChild(current.txtElement);
|
||||
|
||||
this.tgrp.appendChild(current.txtElement);
|
||||
|
||||
this._ensureTransformGroup().appendChild(current.txtElement);
|
||||
},
|
||||
|
||||
setLeadingMoveText: function SVGGraphics_setLeadingMoveText(x, y) {
|
||||
@ -817,16 +807,7 @@ var SVGGraphics = (function SVGGraphicsClosure() {
|
||||
current.xcoords = [];
|
||||
},
|
||||
|
||||
endText: function SVGGraphics_endText() {
|
||||
if (this.current.pendingClip) {
|
||||
this.cgrp.appendChild(this.tgrp);
|
||||
this.pgrp.appendChild(this.cgrp);
|
||||
} else {
|
||||
this.pgrp.appendChild(this.tgrp);
|
||||
}
|
||||
this.tgrp = document.createElementNS(NS, 'svg:g');
|
||||
this.tgrp.setAttributeNS(null, 'transform', pm(this.transformMatrix));
|
||||
},
|
||||
endText: function SVGGraphics_endText() {},
|
||||
|
||||
// Path properties
|
||||
setLineWidth: function SVGGraphics_setLineWidth(width) {
|
||||
@ -924,30 +905,15 @@ var SVGGraphics = (function SVGGraphicsClosure() {
|
||||
pf(current.dashPhase) + 'px');
|
||||
current.path.setAttributeNS(null, 'fill', 'none');
|
||||
|
||||
this.tgrp.appendChild(current.path);
|
||||
if (current.pendingClip) {
|
||||
this.cgrp.appendChild(this.tgrp);
|
||||
this.pgrp.appendChild(this.cgrp);
|
||||
} else {
|
||||
this.pgrp.appendChild(this.tgrp);
|
||||
}
|
||||
this._ensureTransformGroup().appendChild(current.path);
|
||||
|
||||
// Saving a reference in current.element so that it can be addressed
|
||||
// in 'fill' and 'stroke'
|
||||
current.element = current.path;
|
||||
current.setCurrentPoint(x, y);
|
||||
},
|
||||
|
||||
endPath: function SVGGraphics_endPath() {
|
||||
var current = this.current;
|
||||
if (current.pendingClip) {
|
||||
this.cgrp.appendChild(this.tgrp);
|
||||
this.pgrp.appendChild(this.cgrp);
|
||||
} else {
|
||||
this.pgrp.appendChild(this.tgrp);
|
||||
}
|
||||
this.tgrp = document.createElementNS(NS, 'svg:g');
|
||||
this.tgrp.setAttributeNS(null, 'transform', pm(this.transformMatrix));
|
||||
},
|
||||
endPath: function SVGGraphics_endPath() {},
|
||||
|
||||
clip: function SVGGraphics_clip(type) {
|
||||
var current = this.current;
|
||||
@ -966,12 +932,12 @@ var SVGGraphics = (function SVGGraphicsClosure() {
|
||||
this.clippath.appendChild(clipElement);
|
||||
this.defs.appendChild(this.clippath);
|
||||
|
||||
// Create a new group with that attribute
|
||||
// Create a clipping group that references the clipping path.
|
||||
current.pendingClip = true;
|
||||
this.cgrp = document.createElementNS(NS, 'svg:g');
|
||||
this.cgrp.setAttributeNS(null, 'clip-path',
|
||||
'url(#' + current.clipId + ')');
|
||||
this.pgrp.appendChild(this.cgrp);
|
||||
this.svg.appendChild(this.cgrp);
|
||||
},
|
||||
|
||||
closePath: function SVGGraphics_closePath() {
|
||||
@ -1015,20 +981,11 @@ var SVGGraphics = (function SVGGraphicsClosure() {
|
||||
case 'D':
|
||||
this.setDash(value[0], value[1]);
|
||||
break;
|
||||
case 'RI':
|
||||
break;
|
||||
case 'FL':
|
||||
break;
|
||||
case 'Font':
|
||||
this.setFont(value);
|
||||
break;
|
||||
case 'CA':
|
||||
break;
|
||||
case 'ca':
|
||||
break;
|
||||
case 'BM':
|
||||
break;
|
||||
case 'SMask':
|
||||
default:
|
||||
warn('Unimplemented graphic state ' + key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1082,11 +1039,11 @@ var SVGGraphics = (function SVGGraphicsClosure() {
|
||||
rect.setAttributeNS(null, 'width', '1px');
|
||||
rect.setAttributeNS(null, 'height', '1px');
|
||||
rect.setAttributeNS(null, 'fill', current.fillColor);
|
||||
this.tgrp.appendChild(rect);
|
||||
|
||||
this._ensureTransformGroup().appendChild(rect);
|
||||
},
|
||||
|
||||
paintJpegXObject: function SVGGraphics_paintJpegXObject(objId, w, h) {
|
||||
var current = this.current;
|
||||
var imgObj = this.objs.get(objId);
|
||||
var imgEl = document.createElementNS(NS, 'svg:image');
|
||||
imgEl.setAttributeNS(XLINK_NS, 'xlink:href', imgObj.src);
|
||||
@ -1097,13 +1054,7 @@ var SVGGraphics = (function SVGGraphicsClosure() {
|
||||
imgEl.setAttributeNS(null, 'transform',
|
||||
'scale(' + pf(1 / w) + ' ' + pf(-1 / h) + ')');
|
||||
|
||||
this.tgrp.appendChild(imgEl);
|
||||
if (current.pendingClip) {
|
||||
this.cgrp.appendChild(this.tgrp);
|
||||
this.pgrp.appendChild(this.cgrp);
|
||||
} else {
|
||||
this.pgrp.appendChild(this.tgrp);
|
||||
}
|
||||
this._ensureTransformGroup().appendChild(imgEl);
|
||||
},
|
||||
|
||||
paintImageXObject: function SVGGraphics_paintImageXObject(objId) {
|
||||
@ -1117,7 +1068,6 @@ var SVGGraphics = (function SVGGraphicsClosure() {
|
||||
|
||||
paintInlineImageXObject:
|
||||
function SVGGraphics_paintInlineImageXObject(imgData, mask) {
|
||||
var current = this.current;
|
||||
var width = imgData.width;
|
||||
var height = imgData.height;
|
||||
|
||||
@ -1127,7 +1077,7 @@ var SVGGraphics = (function SVGGraphicsClosure() {
|
||||
cliprect.setAttributeNS(null, 'y', '0');
|
||||
cliprect.setAttributeNS(null, 'width', pf(width));
|
||||
cliprect.setAttributeNS(null, 'height', pf(height));
|
||||
current.element = cliprect;
|
||||
this.current.element = cliprect;
|
||||
this.clip('nonzero');
|
||||
var imgEl = document.createElementNS(NS, 'svg:image');
|
||||
imgEl.setAttributeNS(XLINK_NS, 'xlink:href', imgSrc);
|
||||
@ -1141,13 +1091,7 @@ var SVGGraphics = (function SVGGraphicsClosure() {
|
||||
if (mask) {
|
||||
mask.appendChild(imgEl);
|
||||
} else {
|
||||
this.tgrp.appendChild(imgEl);
|
||||
}
|
||||
if (current.pendingClip) {
|
||||
this.cgrp.appendChild(this.tgrp);
|
||||
this.pgrp.appendChild(this.cgrp);
|
||||
} else {
|
||||
this.pgrp.appendChild(this.tgrp);
|
||||
this._ensureTransformGroup().appendChild(imgEl);
|
||||
}
|
||||
},
|
||||
|
||||
@ -1170,15 +1114,14 @@ var SVGGraphics = (function SVGGraphicsClosure() {
|
||||
rect.setAttributeNS(null, 'fill', fillColor);
|
||||
rect.setAttributeNS(null, 'mask', 'url(#' + current.maskId +')');
|
||||
this.defs.appendChild(mask);
|
||||
this.tgrp.appendChild(rect);
|
||||
|
||||
this._ensureTransformGroup().appendChild(rect);
|
||||
|
||||
this.paintInlineImageXObject(imgData, mask);
|
||||
},
|
||||
|
||||
paintFormXObjectBegin:
|
||||
function SVGGraphics_paintFormXObjectBegin(matrix, bbox) {
|
||||
this.save();
|
||||
|
||||
if (isArray(matrix) && matrix.length === 6) {
|
||||
this.transform(matrix[0], matrix[1], matrix[2],
|
||||
matrix[3], matrix[4], matrix[5]);
|
||||
@ -1200,8 +1143,23 @@ var SVGGraphics = (function SVGGraphicsClosure() {
|
||||
},
|
||||
|
||||
paintFormXObjectEnd:
|
||||
function SVGGraphics_paintFormXObjectEnd() {
|
||||
this.restore();
|
||||
function SVGGraphics_paintFormXObjectEnd() {},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_ensureTransformGroup: function SVGGraphics_ensureTransformGroup() {
|
||||
if (!this.tgrp) {
|
||||
this.tgrp = document.createElementNS(NS, 'svg:g');
|
||||
this.tgrp.setAttributeNS(null, 'transform', pm(this.transformMatrix));
|
||||
|
||||
if (this.current.pendingClip) {
|
||||
this.cgrp.appendChild(this.tgrp);
|
||||
} else {
|
||||
this.svg.appendChild(this.tgrp);
|
||||
}
|
||||
}
|
||||
return this.tgrp;
|
||||
}
|
||||
};
|
||||
return SVGGraphics;
|
||||
|
Loading…
Reference in New Issue
Block a user