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.transformMatrix = this.transformStack.pop();
 | 
				
			||||||
      this.current = this.extraStack.pop();
 | 
					      this.current = this.extraStack.pop();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      this.tgrp = document.createElementNS(NS, 'svg:g');
 | 
					      this.tgrp = null;
 | 
				
			||||||
      this.tgrp.setAttributeNS(null, 'transform', pm(this.transformMatrix));
 | 
					 | 
				
			||||||
      this.pgrp.appendChild(this.tgrp);
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    group: function SVGGraphics_group(items) {
 | 
					    group: function SVGGraphics_group(items) {
 | 
				
			||||||
@ -453,9 +451,7 @@ var SVGGraphics = (function SVGGraphicsClosure() {
 | 
				
			|||||||
      var transformMatrix = [a, b, c, d, e, f];
 | 
					      var transformMatrix = [a, b, c, d, e, f];
 | 
				
			||||||
      this.transformMatrix = Util.transform(this.transformMatrix,
 | 
					      this.transformMatrix = Util.transform(this.transformMatrix,
 | 
				
			||||||
                                            transformMatrix);
 | 
					                                            transformMatrix);
 | 
				
			||||||
 | 
					      this.tgrp = null;
 | 
				
			||||||
      this.tgrp = document.createElementNS(NS, 'svg:g');
 | 
					 | 
				
			||||||
      this.tgrp.setAttributeNS(null, 'transform', pm(this.transformMatrix));
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    getSVG: function SVGGraphics_getSVG(operatorList, viewport) {
 | 
					    getSVG: function SVGGraphics_getSVG(operatorList, viewport) {
 | 
				
			||||||
@ -464,14 +460,9 @@ var SVGGraphics = (function SVGGraphicsClosure() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      return this.loadDependencies(operatorList).then(function () {
 | 
					      return this.loadDependencies(operatorList).then(function () {
 | 
				
			||||||
        this.transformMatrix = IDENTITY_MATRIX;
 | 
					        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.defs = document.createElementNS(NS, 'svg:defs');
 | 
				
			||||||
        this.pgrp.appendChild(this.defs);
 | 
					        this.svg.setAttributeNS(null, 'transform', pm(viewport.transform));
 | 
				
			||||||
        this.pgrp.appendChild(this.tgrp);
 | 
					        this.svg.appendChild(this.defs);
 | 
				
			||||||
        this.svg.appendChild(this.pgrp);
 | 
					 | 
				
			||||||
        var opTree = this.convertOpList(operatorList);
 | 
					        var opTree = this.convertOpList(operatorList);
 | 
				
			||||||
        this.executeOpTree(opTree);
 | 
					        this.executeOpTree(opTree);
 | 
				
			||||||
        return this.svg;
 | 
					        return this.svg;
 | 
				
			||||||
@ -633,7 +624,7 @@ var SVGGraphics = (function SVGGraphicsClosure() {
 | 
				
			|||||||
            this.group(opTree[x].items);
 | 
					            this.group(opTree[x].items);
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
          default:
 | 
					          default:
 | 
				
			||||||
            warn('Unimplemented method '+ fn);
 | 
					            warn('Unimplemented operator ' + fn);
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@ -759,8 +750,7 @@ var SVGGraphics = (function SVGGraphicsClosure() {
 | 
				
			|||||||
      current.txtElement.appendChild(current.tspan);
 | 
					      current.txtElement.appendChild(current.tspan);
 | 
				
			||||||
      current.txtgrp.appendChild(current.txtElement);
 | 
					      current.txtgrp.appendChild(current.txtElement);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      this.tgrp.appendChild(current.txtElement);
 | 
					      this._ensureTransformGroup().appendChild(current.txtElement);
 | 
				
			||||||
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    setLeadingMoveText: function SVGGraphics_setLeadingMoveText(x, y) {
 | 
					    setLeadingMoveText: function SVGGraphics_setLeadingMoveText(x, y) {
 | 
				
			||||||
@ -817,16 +807,7 @@ var SVGGraphics = (function SVGGraphicsClosure() {
 | 
				
			|||||||
      current.xcoords = [];
 | 
					      current.xcoords = [];
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    endText: function SVGGraphics_endText() {
 | 
					    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));
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Path properties
 | 
					    // Path properties
 | 
				
			||||||
    setLineWidth: function SVGGraphics_setLineWidth(width) {
 | 
					    setLineWidth: function SVGGraphics_setLineWidth(width) {
 | 
				
			||||||
@ -924,30 +905,15 @@ var SVGGraphics = (function SVGGraphicsClosure() {
 | 
				
			|||||||
                                  pf(current.dashPhase) + 'px');
 | 
					                                  pf(current.dashPhase) + 'px');
 | 
				
			||||||
      current.path.setAttributeNS(null, 'fill', 'none');
 | 
					      current.path.setAttributeNS(null, 'fill', 'none');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      this.tgrp.appendChild(current.path);
 | 
					      this._ensureTransformGroup().appendChild(current.path);
 | 
				
			||||||
      if (current.pendingClip) {
 | 
					
 | 
				
			||||||
        this.cgrp.appendChild(this.tgrp);
 | 
					 | 
				
			||||||
        this.pgrp.appendChild(this.cgrp);
 | 
					 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        this.pgrp.appendChild(this.tgrp);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      // Saving a reference in current.element so that it can be addressed
 | 
					      // Saving a reference in current.element so that it can be addressed
 | 
				
			||||||
      // in 'fill' and 'stroke'
 | 
					      // in 'fill' and 'stroke'
 | 
				
			||||||
      current.element = current.path;
 | 
					      current.element = current.path;
 | 
				
			||||||
      current.setCurrentPoint(x, y);
 | 
					      current.setCurrentPoint(x, y);
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    endPath: function SVGGraphics_endPath() {
 | 
					    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));
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    clip: function SVGGraphics_clip(type) {
 | 
					    clip: function SVGGraphics_clip(type) {
 | 
				
			||||||
      var current = this.current;
 | 
					      var current = this.current;
 | 
				
			||||||
@ -966,12 +932,12 @@ var SVGGraphics = (function SVGGraphicsClosure() {
 | 
				
			|||||||
      this.clippath.appendChild(clipElement);
 | 
					      this.clippath.appendChild(clipElement);
 | 
				
			||||||
      this.defs.appendChild(this.clippath);
 | 
					      this.defs.appendChild(this.clippath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // Create a new group with that attribute
 | 
					      // Create a clipping group that references the clipping path.
 | 
				
			||||||
      current.pendingClip = true;
 | 
					      current.pendingClip = true;
 | 
				
			||||||
      this.cgrp = document.createElementNS(NS, 'svg:g');
 | 
					      this.cgrp = document.createElementNS(NS, 'svg:g');
 | 
				
			||||||
      this.cgrp.setAttributeNS(null, 'clip-path',
 | 
					      this.cgrp.setAttributeNS(null, 'clip-path',
 | 
				
			||||||
                               'url(#' + current.clipId + ')');
 | 
					                               'url(#' + current.clipId + ')');
 | 
				
			||||||
      this.pgrp.appendChild(this.cgrp);
 | 
					      this.svg.appendChild(this.cgrp);
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    closePath: function SVGGraphics_closePath() {
 | 
					    closePath: function SVGGraphics_closePath() {
 | 
				
			||||||
@ -1015,20 +981,11 @@ var SVGGraphics = (function SVGGraphicsClosure() {
 | 
				
			|||||||
          case 'D':
 | 
					          case 'D':
 | 
				
			||||||
            this.setDash(value[0], value[1]);
 | 
					            this.setDash(value[0], value[1]);
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
          case 'RI':
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
          case 'FL':
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
          case 'Font':
 | 
					          case 'Font':
 | 
				
			||||||
            this.setFont(value);
 | 
					            this.setFont(value);
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
          case 'CA':
 | 
					          default:
 | 
				
			||||||
            break;
 | 
					            warn('Unimplemented graphic state ' + key);
 | 
				
			||||||
          case 'ca':
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
          case 'BM':
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
          case 'SMask':
 | 
					 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@ -1082,11 +1039,11 @@ var SVGGraphics = (function SVGGraphicsClosure() {
 | 
				
			|||||||
      rect.setAttributeNS(null, 'width', '1px');
 | 
					      rect.setAttributeNS(null, 'width', '1px');
 | 
				
			||||||
      rect.setAttributeNS(null, 'height', '1px');
 | 
					      rect.setAttributeNS(null, 'height', '1px');
 | 
				
			||||||
      rect.setAttributeNS(null, 'fill', current.fillColor);
 | 
					      rect.setAttributeNS(null, 'fill', current.fillColor);
 | 
				
			||||||
      this.tgrp.appendChild(rect);
 | 
					
 | 
				
			||||||
 | 
					      this._ensureTransformGroup().appendChild(rect);
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    paintJpegXObject: function SVGGraphics_paintJpegXObject(objId, w, h) {
 | 
					    paintJpegXObject: function SVGGraphics_paintJpegXObject(objId, w, h) {
 | 
				
			||||||
      var current = this.current;
 | 
					 | 
				
			||||||
      var imgObj = this.objs.get(objId);
 | 
					      var imgObj = this.objs.get(objId);
 | 
				
			||||||
      var imgEl = document.createElementNS(NS, 'svg:image');
 | 
					      var imgEl = document.createElementNS(NS, 'svg:image');
 | 
				
			||||||
      imgEl.setAttributeNS(XLINK_NS, 'xlink:href', imgObj.src);
 | 
					      imgEl.setAttributeNS(XLINK_NS, 'xlink:href', imgObj.src);
 | 
				
			||||||
@ -1097,13 +1054,7 @@ var SVGGraphics = (function SVGGraphicsClosure() {
 | 
				
			|||||||
      imgEl.setAttributeNS(null, 'transform',
 | 
					      imgEl.setAttributeNS(null, 'transform',
 | 
				
			||||||
                           'scale(' + pf(1 / w) + ' ' + pf(-1 / h) + ')');
 | 
					                           'scale(' + pf(1 / w) + ' ' + pf(-1 / h) + ')');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      this.tgrp.appendChild(imgEl);
 | 
					      this._ensureTransformGroup().appendChild(imgEl);
 | 
				
			||||||
      if (current.pendingClip) {
 | 
					 | 
				
			||||||
        this.cgrp.appendChild(this.tgrp);
 | 
					 | 
				
			||||||
        this.pgrp.appendChild(this.cgrp);
 | 
					 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        this.pgrp.appendChild(this.tgrp);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    paintImageXObject: function SVGGraphics_paintImageXObject(objId) {
 | 
					    paintImageXObject: function SVGGraphics_paintImageXObject(objId) {
 | 
				
			||||||
@ -1117,7 +1068,6 @@ var SVGGraphics = (function SVGGraphicsClosure() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    paintInlineImageXObject:
 | 
					    paintInlineImageXObject:
 | 
				
			||||||
        function SVGGraphics_paintInlineImageXObject(imgData, mask) {
 | 
					        function SVGGraphics_paintInlineImageXObject(imgData, mask) {
 | 
				
			||||||
      var current = this.current;
 | 
					 | 
				
			||||||
      var width = imgData.width;
 | 
					      var width = imgData.width;
 | 
				
			||||||
      var height = imgData.height;
 | 
					      var height = imgData.height;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1127,7 +1077,7 @@ var SVGGraphics = (function SVGGraphicsClosure() {
 | 
				
			|||||||
      cliprect.setAttributeNS(null, 'y', '0');
 | 
					      cliprect.setAttributeNS(null, 'y', '0');
 | 
				
			||||||
      cliprect.setAttributeNS(null, 'width', pf(width));
 | 
					      cliprect.setAttributeNS(null, 'width', pf(width));
 | 
				
			||||||
      cliprect.setAttributeNS(null, 'height', pf(height));
 | 
					      cliprect.setAttributeNS(null, 'height', pf(height));
 | 
				
			||||||
      current.element = cliprect;
 | 
					      this.current.element = cliprect;
 | 
				
			||||||
      this.clip('nonzero');
 | 
					      this.clip('nonzero');
 | 
				
			||||||
      var imgEl = document.createElementNS(NS, 'svg:image');
 | 
					      var imgEl = document.createElementNS(NS, 'svg:image');
 | 
				
			||||||
      imgEl.setAttributeNS(XLINK_NS, 'xlink:href', imgSrc);
 | 
					      imgEl.setAttributeNS(XLINK_NS, 'xlink:href', imgSrc);
 | 
				
			||||||
@ -1141,13 +1091,7 @@ var SVGGraphics = (function SVGGraphicsClosure() {
 | 
				
			|||||||
      if (mask) {
 | 
					      if (mask) {
 | 
				
			||||||
        mask.appendChild(imgEl);
 | 
					        mask.appendChild(imgEl);
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        this.tgrp.appendChild(imgEl);
 | 
					        this._ensureTransformGroup().appendChild(imgEl);
 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      if (current.pendingClip) {
 | 
					 | 
				
			||||||
        this.cgrp.appendChild(this.tgrp);
 | 
					 | 
				
			||||||
        this.pgrp.appendChild(this.cgrp);
 | 
					 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        this.pgrp.appendChild(this.tgrp);
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1170,15 +1114,14 @@ var SVGGraphics = (function SVGGraphicsClosure() {
 | 
				
			|||||||
      rect.setAttributeNS(null, 'fill', fillColor);
 | 
					      rect.setAttributeNS(null, 'fill', fillColor);
 | 
				
			||||||
      rect.setAttributeNS(null, 'mask', 'url(#' + current.maskId +')');
 | 
					      rect.setAttributeNS(null, 'mask', 'url(#' + current.maskId +')');
 | 
				
			||||||
      this.defs.appendChild(mask);
 | 
					      this.defs.appendChild(mask);
 | 
				
			||||||
      this.tgrp.appendChild(rect);
 | 
					
 | 
				
			||||||
 | 
					      this._ensureTransformGroup().appendChild(rect);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      this.paintInlineImageXObject(imgData, mask);
 | 
					      this.paintInlineImageXObject(imgData, mask);
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    paintFormXObjectBegin:
 | 
					    paintFormXObjectBegin:
 | 
				
			||||||
        function SVGGraphics_paintFormXObjectBegin(matrix, bbox) {
 | 
					        function SVGGraphics_paintFormXObjectBegin(matrix, bbox) {
 | 
				
			||||||
      this.save();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      if (isArray(matrix) && matrix.length === 6) {
 | 
					      if (isArray(matrix) && matrix.length === 6) {
 | 
				
			||||||
        this.transform(matrix[0], matrix[1], matrix[2],
 | 
					        this.transform(matrix[0], matrix[1], matrix[2],
 | 
				
			||||||
                       matrix[3], matrix[4], matrix[5]);
 | 
					                       matrix[3], matrix[4], matrix[5]);
 | 
				
			||||||
@ -1200,8 +1143,23 @@ var SVGGraphics = (function SVGGraphicsClosure() {
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    paintFormXObjectEnd:
 | 
					    paintFormXObjectEnd:
 | 
				
			||||||
        function SVGGraphics_paintFormXObjectEnd() {
 | 
					        function SVGGraphics_paintFormXObjectEnd() {},
 | 
				
			||||||
      this.restore();
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @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;
 | 
					  return SVGGraphics;
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user