1
+ const twgl = require ( 'twgl.js' ) ;
2
+
1
3
const Skin = require ( './Skin' ) ;
2
- const SVGMIP = require ( './SVGMIP' ) ;
3
4
const SvgRenderer = require ( 'scratch-svg-renderer' ) . SVGRenderer ;
4
5
5
6
const MAX_TEXTURE_DIMENSION = 2048 ;
@@ -29,12 +30,12 @@ class SVGSkin extends Skin {
29
30
/** @type {SvgRenderer } */
30
31
this . _svgRenderer = new SvgRenderer ( ) ;
31
32
32
- /** @type {WebGLTexture } */
33
- this . _texture = null ;
34
-
35
- /** @type {Array.<SVGMIPs> } */
33
+ /** @type {Array<WebGLTexture> } */
36
34
this . _scaledMIPs = [ ] ;
37
35
36
+ /** @type {number } */
37
+ this . _largestMIPScale = 0 ;
38
+
38
39
/**
39
40
* Ratio of the size of the SVG and the max size of the WebGL texture
40
41
* @type {Number }
@@ -46,15 +47,7 @@ class SVGSkin extends Skin {
46
47
* Dispose of this object. Do not use it after calling this method.
47
48
*/
48
49
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 ( ) ;
58
51
super . dispose ( ) ;
59
52
}
60
53
@@ -76,24 +69,33 @@ class SVGSkin extends Skin {
76
69
}
77
70
78
71
/**
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.
81
73
* @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.
84
74
* @return {SVGMIP } An object that handles creating and updating SVG textures.
85
75
*/
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
95
90
} ;
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
+ }
97
99
98
100
return mip ;
99
101
}
@@ -125,31 +127,23 @@ class SVGSkin extends Skin {
125
127
}
126
128
}
127
129
128
- if ( ! this . _scaledMIPs [ textureIndex + INDEX_OFFSET ] ) {
130
+ if ( this . _svgRenderer . loaded && ! this . _scaledMIPs [ textureIndex + INDEX_OFFSET ] ) {
129
131
this . _scaledMIPs [ textureIndex + INDEX_OFFSET ] = this . createMIP ( newScale ) ;
130
132
}
131
133
132
- return this . _scaledMIPs [ textureIndex + INDEX_OFFSET ] . getTexture ( ) ;
134
+ return this . _scaledMIPs [ textureIndex + INDEX_OFFSET ] || super . getTexture ( ) ;
133
135
}
134
136
135
137
/**
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.
139
139
* @param {Array<number> } [rotationCenter] - Optional rotation center for the SVG. If not supplied, it will be
140
140
* calculated from the bounding box
141
- * @fires Skin.event:WasAltered
141
+ * @fires Skin.event:WasAltered
142
142
*/
143
- resetMIPs ( mip , rotationCenter ) {
144
- this . _scaledMIPs . forEach ( oldMIP => oldMIP . dispose ( ) ) ;
143
+ resetMIPs ( ) {
144
+ this . _scaledMIPs . forEach ( oldMIP => this . _renderer . gl . deleteTexture ( oldMIP ) ) ;
145
145
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 ;
153
147
}
154
148
155
149
/**
@@ -158,22 +152,25 @@ class SVGSkin extends Skin {
158
152
* @param {Array<number> } [rotationCenter] - Optional rotation center for the SVG.
159
153
*/
160
154
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
+ }
162
161
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
+ }
167
167
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 ( ) ;
173
169
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
+ } ) ;
177
174
}
178
175
179
176
}
0 commit comments