Skip to content

Commit

Permalink
Cache bounding boxes in gradient transformation
Browse files Browse the repository at this point in the history
  • Loading branch information
GarboMuffin committed May 19, 2024
1 parent b50e7bb commit c1d0698
Showing 1 changed file with 34 additions and 20 deletions.
54 changes: 34 additions & 20 deletions src/transform-applier.js
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,39 @@ const _parseUrl = (value, windowRef) => {
return res;
};

const pathBBoxCache = Object.create(null);

/**
* @param {SVGPathElement} element The SVG <path> element
* @param {Window} windowRef Window object
* @returns {DOMRect} Bounding box
*/
const getPathBBox = (element, windowRef) => {
const d = element.attributes.d.value;
if (Object.prototype.hasOwnProperty.call(pathBBoxCache, d)) {
return pathBBoxCache[d];
}

const doc = windowRef.document;
const svgSpot = doc.createElement('span');
try {
doc.body.appendChild(svgSpot);
/** @type {SVGSVGElement} */
const svg = doc.createElementNS(SvgElement.svg, 'svg');
/** @type {SVGPathElement} */
const path = doc.createElementNS(SvgElement.svg, 'path');
path.setAttribute('d', d);
svg.appendChild(path);
svgSpot.appendChild(svg);
const bbox = svg.getBBox();
pathBBoxCache[d] = bbox;
return bbox;
} finally {
// Always destroy the element, even if, for example, getBBox throws.
doc.body.removeChild(svgSpot);
}
};

/**
* Scratch 2.0 displays stroke widths in a "normalized" way, that is,
* if a shape with a stroke width has a transform applied, it will be
Expand Down Expand Up @@ -559,27 +592,8 @@ const transformStrokeWidths = function (svgTag, windowRef, bboxForTesting) {
const strokeGradientId = _parseUrl(stroke, windowRef);

if (fillGradientId || strokeGradientId) {
const doc = windowRef.document;
// Need path bounds to transform gradient
const svgSpot = doc.createElement('span');
let bbox;
if (bboxForTesting) {
bbox = bboxForTesting;
} else {
try {
doc.body.appendChild(svgSpot);
const svg = SvgElement.set(doc.createElementNS(SvgElement.svg, 'svg'));
const path = SvgElement.set(doc.createElementNS(SvgElement.svg, 'path'));
path.setAttribute('d', element.attributes.d.value);
svg.appendChild(path);
svgSpot.appendChild(svg);
// Take the bounding box.
bbox = svg.getBBox();
} finally {
// Always destroy the element, even if, for example, getBBox throws.
doc.body.removeChild(svgSpot);
}
}
const bbox = bboxForTesting || getPathBBox(element, windowRef);

if (fillGradientId) {
const newFillRef = _createGradient(fillGradientId, svgTag, bbox, matrix);
Expand Down

0 comments on commit c1d0698

Please sign in to comment.