Skip to content

Commit 478698c

Browse files
authored
Merge pull request #527 from adroitwhiz/svgmip-synchronous
Synchronous SVG mipmaps
2 parents 8409de7 + 6662a7d commit 478698c

File tree

2 files changed

+53
-112
lines changed

2 files changed

+53
-112
lines changed

src/SVGMIP.js

Lines changed: 0 additions & 56 deletions
This file was deleted.

src/SVGSkin.js

Lines changed: 53 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
const twgl = require('twgl.js');
2+
13
const Skin = require('./Skin');
2-
const SVGMIP = require('./SVGMIP');
34
const SvgRenderer = require('scratch-svg-renderer').SVGRenderer;
45

56
const MAX_TEXTURE_DIMENSION = 2048;
@@ -29,12 +30,12 @@ class SVGSkin extends Skin {
2930
/** @type {SvgRenderer} */
3031
this._svgRenderer = new SvgRenderer();
3132

32-
/** @type {WebGLTexture} */
33-
this._texture = null;
34-
35-
/** @type {Array.<SVGMIPs>} */
33+
/** @type {Array<WebGLTexture>} */
3634
this._scaledMIPs = [];
3735

36+
/** @type {number} */
37+
this._largestMIPScale = 0;
38+
3839
/**
3940
* Ratio of the size of the SVG and the max size of the WebGL texture
4041
* @type {Number}
@@ -46,15 +47,7 @@ class SVGSkin extends Skin {
4647
* Dispose of this object. Do not use it after calling this method.
4748
*/
4849
dispose () {
49-
if (this._texture) {
50-
for (const mip of this._scaledMIPs) {
51-
if (mip) {
52-
mip.dispose();
53-
}
54-
}
55-
this._texture = null;
56-
this._scaledMIPs.length = 0;
57-
}
50+
this.resetMIPs();
5851
super.dispose();
5952
}
6053

@@ -76,24 +69,33 @@ class SVGSkin extends Skin {
7669
}
7770

7871
/**
79-
* Create a MIP for a given scale and pass it a callback for updating
80-
* state when switching between scales and MIPs.
72+
* Create a MIP for a given scale.
8173
* @param {number} scale - The relative size of the MIP
82-
* @param {function} resetCallback - this is a callback for doing a hard reset
83-
* of MIPs and a reset of the rotation center. Only passed in if the MIP scale is 1.
8474
* @return {SVGMIP} An object that handles creating and updating SVG textures.
8575
*/
86-
createMIP (scale, resetCallback) {
87-
const textureCallback = textureData => {
88-
if (resetCallback) resetCallback();
89-
// Check if we have the largest MIP
90-
// eslint-disable-next-line no-use-before-define
91-
if (!this._scaledMIPs.length || this._scaledMIPs[this._scaledMIPs.length - 1]._scale <= scale) {
92-
// Currently silhouette only gets scaled up
93-
this._silhouette.update(textureData);
94-
}
76+
createMIP (scale) {
77+
this._svgRenderer.draw(scale);
78+
79+
// Pull out the ImageData from the canvas. ImageData speeds up
80+
// updating Silhouette and is better handled by more browsers in
81+
// regards to memory.
82+
const canvas = this._svgRenderer.canvas;
83+
const context = canvas.getContext('2d');
84+
const textureData = context.getImageData(0, 0, canvas.width, canvas.height);
85+
86+
const textureOptions = {
87+
auto: false,
88+
wrap: this._renderer.gl.CLAMP_TO_EDGE,
89+
src: textureData
9590
};
96-
const mip = new SVGMIP(this._renderer, this._svgRenderer, scale, textureCallback);
91+
92+
const mip = twgl.createTexture(this._renderer.gl, textureOptions);
93+
94+
// Check if this is the largest MIP created so far. Currently, silhouettes only get scaled up.
95+
if (this._largestMIPScale < scale) {
96+
this._silhouette.update(textureData);
97+
this._largestMIPScale = scale;
98+
}
9799

98100
return mip;
99101
}
@@ -125,31 +127,23 @@ class SVGSkin extends Skin {
125127
}
126128
}
127129

128-
if (!this._scaledMIPs[textureIndex + INDEX_OFFSET]) {
130+
if (this._svgRenderer.loaded && !this._scaledMIPs[textureIndex + INDEX_OFFSET]) {
129131
this._scaledMIPs[textureIndex + INDEX_OFFSET] = this.createMIP(newScale);
130132
}
131133

132-
return this._scaledMIPs[textureIndex + INDEX_OFFSET].getTexture();
134+
return this._scaledMIPs[textureIndex + INDEX_OFFSET] || super.getTexture();
133135
}
134136

135137
/**
136-
* Do a hard reset of the existing MIPs by calling dispose(), setting a new
137-
* scale 1 MIP in this._scaledMIPs, and finally updating the rotationCenter.
138-
* @param {SVGMIPs} mip - An object that handles creating and updating SVG textures.
138+
* Do a hard reset of the existing MIPs by deleting them.
139139
* @param {Array<number>} [rotationCenter] - Optional rotation center for the SVG. If not supplied, it will be
140140
* calculated from the bounding box
141-
* @fires Skin.event:WasAltered
141+
* @fires Skin.event:WasAltered
142142
*/
143-
resetMIPs (mip, rotationCenter) {
144-
this._scaledMIPs.forEach(oldMIP => oldMIP.dispose());
143+
resetMIPs () {
144+
this._scaledMIPs.forEach(oldMIP => this._renderer.gl.deleteTexture(oldMIP));
145145
this._scaledMIPs.length = 0;
146-
147-
// Set new scale 1 MIP after outdated MIPs have been disposed
148-
this._texture = this._scaledMIPs[INDEX_OFFSET] = mip;
149-
150-
if (typeof rotationCenter === 'undefined') rotationCenter = this.calculateRotationCenter();
151-
this.setRotationCenter.apply(this, rotationCenter);
152-
this.emit(Skin.Events.WasAltered);
146+
this._largestMIPScale = 0;
153147
}
154148

155149
/**
@@ -158,22 +152,25 @@ class SVGSkin extends Skin {
158152
* @param {Array<number>} [rotationCenter] - Optional rotation center for the SVG.
159153
*/
160154
setSVG (svgData, rotationCenter) {
161-
this._svgRenderer.loadString(svgData);
155+
this._svgRenderer.loadSVG(svgData, false, () => {
156+
const svgSize = this._svgRenderer.size;
157+
if (svgSize[0] === 0 || svgSize[1] === 0) {
158+
super.setEmptyImageData();
159+
return;
160+
}
162161

163-
if (!this._svgRenderer.canvas.width || !this._svgRenderer.canvas.height) {
164-
super.setEmptyImageData();
165-
return;
166-
}
162+
const maxDimension = Math.ceil(Math.max(this.size[0], this.size[1]));
163+
let testScale = 2;
164+
for (testScale; maxDimension * testScale <= MAX_TEXTURE_DIMENSION; testScale *= 2) {
165+
this._maxTextureScale = testScale;
166+
}
167167

168-
const maxDimension = Math.ceil(Math.max(this.size[0], this.size[1]));
169-
let testScale = 2;
170-
for (testScale; maxDimension * testScale <= MAX_TEXTURE_DIMENSION; testScale *= 2) {
171-
this._maxTextureScale = testScale;
172-
}
168+
this.resetMIPs();
173169

174-
// Create the 1.0 scale MIP at INDEX_OFFSET.
175-
const textureScale = 1;
176-
const mip = this.createMIP(textureScale, () => this.resetMIPs(mip, rotationCenter));
170+
if (typeof rotationCenter === 'undefined') rotationCenter = this.calculateRotationCenter();
171+
this.setRotationCenter.apply(this, rotationCenter);
172+
this.emit(Skin.Events.WasAltered);
173+
});
177174
}
178175

179176
}

0 commit comments

Comments
 (0)