1+ const twgl = require ( 'twgl.js' ) ;
2+
13const Skin = require ( './Skin' ) ;
2- const SVGMIP = require ( './SVGMIP' ) ;
34const SvgRenderer = require ( 'scratch-svg-renderer' ) . SVGRenderer ;
45
56const 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