1- const twgl = require ( 'twgl.js' ) ;
2-
31const Skin = require ( './Skin' ) ;
2+ const SVGMIP = require ( './SVGMIP' ) ;
43const SvgRenderer = require ( 'scratch-svg-renderer' ) . SVGRenderer ;
54
65const MAX_TEXTURE_DIMENSION = 2048 ;
6+ const MIN_TEXTURE_SCALE = 1 / 256 ;
7+ const INDEX_OFFSET = 8 ;
78
89class SVGSkin extends Skin {
910 /**
@@ -25,19 +26,22 @@ class SVGSkin extends Skin {
2526 /** @type {WebGLTexture } */
2627 this . _texture = null ;
2728
28- /** @type {number } */
29- this . _textureScale = 1 ;
29+ /** @type {Array.<SVGMIPs> } */
30+ this . _scaledMIPs = [ ] ;
31+
32+ /** @type {Number } */
33+ this . _maxTextureScale = 1 ;
3034
3135 /** @type {Number } */
32- this . _maxTextureScale = 0 ;
36+ this . _largestTextureScale = 1 ;
3337 }
3438
3539 /**
3640 * Dispose of this object. Do not use it after calling this method.
3741 */
3842 dispose ( ) {
3943 if ( this . _texture ) {
40- this . _renderer . gl . deleteTexture ( this . _texture ) ;
44+ this . _renderer . gl . deleteTexture ( this . _texture . getTexture ( ) ) ;
4145 this . _texture = null ;
4246 }
4347 super . dispose ( ) ;
@@ -60,6 +64,34 @@ class SVGSkin extends Skin {
6064 super . setRotationCenter ( x - viewOffset [ 0 ] , y - viewOffset [ 1 ] ) ;
6165 }
6266
67+ /**
68+ * Create a MIP for a given scale and pass it a callback for updating
69+ * state when switching between scales and MIPs.
70+ * @param {number } scale - The relative size of the MIP
71+ * @return {SVGMIP } An object that handles creating and updating SVG textures.
72+ */
73+ createMIP ( scale ) {
74+ const callback = textureData => {
75+ if ( scale > this . _largestTextureScale ) {
76+ this . _largestTextureScale = scale ;
77+ // Currently silhouette only gets scaled up
78+ this . _silhouette . update ( textureData ) ;
79+ }
80+
81+ if ( scale === 1 ) {
82+ const maxDimension = Math . max ( textureData . width , textureData . height ) ;
83+ let testScale = 2 ;
84+ for ( testScale ; maxDimension * testScale <= MAX_TEXTURE_DIMENSION ; testScale *= 2 ) {
85+ this . _maxTextureScale = testScale ;
86+ }
87+ this . _silhouette . update ( textureData ) ;
88+ }
89+ } ;
90+ const mip = new SVGMIP ( this . _renderer , this . _svgRenderer , scale , callback ) ;
91+
92+ return mip ;
93+ }
94+
6395 /**
6496 * @param {Array<number> } scale - The scaling factors to be used, each in the [0,100] range.
6597 * @return {WebGLTexture } The GL texture representation of this skin when drawing at the given scale.
@@ -69,27 +101,26 @@ class SVGSkin extends Skin {
69101 // The texture only ever gets uniform scale. Take the larger of the two axes.
70102 const scaleMax = scale ? Math . max ( Math . abs ( scale [ 0 ] ) , Math . abs ( scale [ 1 ] ) ) : 100 ;
71103 const requestedScale = Math . min ( scaleMax / 100 , this . _maxTextureScale ) ;
72- let newScale = this . _textureScale ;
73- while ( ( newScale < this . _maxTextureScale ) && ( requestedScale >= 1.5 * newScale ) ) {
74- newScale *= 2 ;
104+ let newScale = 1 ;
105+ let textureIndex = 0 ;
106+
107+ if ( requestedScale < 1 ) {
108+ while ( ( newScale > MIN_TEXTURE_SCALE ) && ( requestedScale <= newScale * .75 ) ) {
109+ newScale /= 2 ;
110+ textureIndex -= 1 ;
111+ }
112+ } else {
113+ while ( ( newScale < this . _maxTextureScale ) && ( requestedScale >= 1.5 * newScale ) ) {
114+ newScale *= 2 ;
115+ textureIndex += 1 ;
116+ }
75117 }
76- if ( this . _textureScale !== newScale ) {
77- this . _textureScale = newScale ;
78- this . _svgRenderer . _draw ( this . _textureScale , ( ) => {
79- if ( this . _textureScale === newScale ) {
80- const canvas = this . _svgRenderer . canvas ;
81- const context = canvas . getContext ( '2d' ) ;
82- const textureData = context . getImageData ( 0 , 0 , canvas . width , canvas . height ) ;
83-
84- const gl = this . _renderer . gl ;
85- gl . bindTexture ( gl . TEXTURE_2D , this . _texture ) ;
86- gl . texImage2D ( gl . TEXTURE_2D , 0 , gl . RGBA , gl . RGBA , gl . UNSIGNED_BYTE , textureData ) ;
87- this . _silhouette . update ( textureData ) ;
88- }
89- } ) ;
118+
119+ if ( ! this . _scaledMIPs [ textureIndex + INDEX_OFFSET ] ) {
120+ this . _scaledMIPs [ textureIndex + INDEX_OFFSET ] = this . createMIP ( newScale ) ;
90121 }
91122
92- return this . _texture ;
123+ return this . _scaledMIPs [ textureIndex + INDEX_OFFSET ] . getTexture ( ) ;
93124 }
94125
95126 /**
@@ -100,43 +131,25 @@ class SVGSkin extends Skin {
100131 * @fires Skin.event:WasAltered
101132 */
102133 setSVG ( svgData , rotationCenter ) {
103- this . _svgRenderer . fromString ( svgData , 1 , ( ) => {
104- const gl = this . _renderer . gl ;
105- this . _textureScale = this . _maxTextureScale = 1 ;
106-
107- // Pull out the ImageData from the canvas. ImageData speeds up
108- // updating Silhouette and is better handled by more browsers in
109- // regards to memory.
110- const canvas = this . _svgRenderer . canvas ;
111- const context = canvas . getContext ( '2d' ) ;
112- const textureData = context . getImageData ( 0 , 0 , canvas . width , canvas . height ) ;
113-
114- if ( this . _texture ) {
115- gl . bindTexture ( gl . TEXTURE_2D , this . _texture ) ;
116- gl . texImage2D ( gl . TEXTURE_2D , 0 , gl . RGBA , gl . RGBA , gl . UNSIGNED_BYTE , textureData ) ;
117- this . _silhouette . update ( textureData ) ;
118- } else {
119- // TODO: mipmaps?
120- const textureOptions = {
121- auto : true ,
122- wrap : gl . CLAMP_TO_EDGE ,
123- src : textureData
124- } ;
125-
126- this . _texture = twgl . createTexture ( gl , textureOptions ) ;
127- this . _silhouette . update ( textureData ) ;
128- }
134+ this . _svgRenderer . loadString ( svgData ) ;
135+
136+ if ( this . _texture ) {
137+ this . _texture = this . _scaledMIPs [ INDEX_OFFSET ] ;
138+ this . _texture . draw ( ) ;
129139
130- const maxDimension = Math . max ( this . _svgRenderer . canvas . width , this . _svgRenderer . canvas . height ) ;
131- let testScale = 2 ;
132- for ( testScale ; maxDimension * testScale <= MAX_TEXTURE_DIMENSION ; testScale *= 2 ) {
133- this . _maxTextureScale = testScale ;
140+ for ( const index in this . _scaledMIPs ) {
141+ if ( index !== INDEX_OFFSET ) {
142+ this . _scaledMIPs [ index ] . dirty = true ;
143+ }
134144 }
145+ } else {
146+ const textureScale = 1 ;
147+ this . _texture = this . _scaledMIPs [ INDEX_OFFSET ] = this . createMIP ( textureScale ) ;
148+ }
135149
136- if ( typeof rotationCenter === 'undefined' ) rotationCenter = this . calculateRotationCenter ( ) ;
137- this . setRotationCenter . apply ( this , rotationCenter ) ;
138- this . emit ( Skin . Events . WasAltered ) ;
139- } ) ;
150+ if ( typeof rotationCenter === 'undefined' ) rotationCenter = this . calculateRotationCenter ( ) ;
151+ this . setRotationCenter . apply ( this , rotationCenter ) ;
152+ this . emit ( Skin . Events . WasAltered ) ;
140153 }
141154
142155}
0 commit comments