@@ -188,9 +188,23 @@ class RenderWebGL extends EventEmitter {
188188        /** @type  {function } */ 
189189        this . _exitRegion  =  null ; 
190190
191+         /** @type  {object } */ 
192+         this . _backgroundDrawRegionId  =  { 
193+             enter : ( )  =>  this . _enterDrawBackground ( ) , 
194+             exit : ( )  =>  this . _exitDrawBackground ( ) 
195+         } ; 
196+ 
191197        /** @type  {Array.<snapshotCallback> } */ 
192198        this . _snapshotCallbacks  =  [ ] ; 
193199
200+         /** @type  {Array<number> } */ 
201+         // Don't set this directly-- use setBackgroundColor so it stays in sync with _backgroundColor3b 
202+         this . _backgroundColor4f  =  [ 0 ,  0 ,  0 ,  1 ] ; 
203+ 
204+         /** @type  {Uint8ClampedArray } */ 
205+         // Don't set this directly-- use setBackgroundColor so it stays in sync with _backgroundColor4f 
206+         this . _backgroundColor3b  =  new  Uint8ClampedArray ( 3 ) ; 
207+ 
194208        this . _createGeometry ( ) ; 
195209
196210        this . on ( RenderConstants . Events . NativeSizeChanged ,  this . onNativeSizeChanged ) ; 
@@ -250,7 +264,14 @@ class RenderWebGL extends EventEmitter {
250264     * @param  {number } blue The blue component for the background. 
251265     */ 
252266    setBackgroundColor  ( red ,  green ,  blue )  { 
253-         this . _backgroundColor  =  [ red ,  green ,  blue ,  1 ] ; 
267+         this . _backgroundColor4f [ 0 ]  =  red ; 
268+         this . _backgroundColor4f [ 1 ]  =  green ; 
269+         this . _backgroundColor4f [ 2 ]  =  blue ; 
270+ 
271+         this . _backgroundColor3b [ 0 ]  =  red  *  255 ; 
272+         this . _backgroundColor3b [ 1 ]  =  green  *  255 ; 
273+         this . _backgroundColor3b [ 2 ]  =  blue  *  255 ; 
274+ 
254275    } 
255276
256277    /** 
@@ -629,7 +650,7 @@ class RenderWebGL extends EventEmitter {
629650
630651        twgl . bindFramebufferInfo ( gl ,  null ) ; 
631652        gl . viewport ( 0 ,  0 ,  gl . canvas . width ,  gl . canvas . height ) ; 
632-         gl . clearColor . apply ( gl ,  this . _backgroundColor ) ; 
653+         gl . clearColor . apply ( gl ,  this . _backgroundColor4f ) ; 
633654        gl . clear ( gl . COLOR_BUFFER_BIT ) ; 
634655
635656        this . _drawThese ( this . _drawList ,  ShaderManager . DRAW_MODE . default ,  this . _projection ) ; 
@@ -745,12 +766,22 @@ class RenderWebGL extends EventEmitter {
745766     */ 
746767    isTouchingColor  ( drawableID ,  color3b ,  mask3b )  { 
747768        const  candidates  =  this . _candidatesTouching ( drawableID ,  this . _visibleDrawList ) ; 
748-         if  ( candidates . length  ===  0 )  { 
769+ 
770+         let  bounds ; 
771+         if  ( colorMatches ( color3b ,  this . _backgroundColor3b ,  0 ) )  { 
772+             // If the color we're checking for is the background color, don't confine the check to 
773+             // candidate drawables' bounds--since the background spans the entire stage, we must check 
774+             // everything that lies inside the drawable. 
775+             bounds  =  this . _touchingBounds ( drawableID ) ; 
776+             // e.g. empty costume, or off the stage 
777+             if  ( bounds  ===  null )  return  false ; 
778+         }  else  if  ( candidates . length  ===  0 )  { 
779+             // If not checking for the background color, we can return early if there are no candidate drawables. 
749780            return  false ; 
781+         }  else  { 
782+             bounds  =  this . _candidatesBounds ( candidates ) ; 
750783        } 
751784
752-         const  bounds  =  this . _candidatesBounds ( candidates ) ; 
753- 
754785        const  maxPixelsForCPU  =  this . _getMaxPixelsForCPU ( ) ; 
755786
756787        const  debugCanvasContext  =  this . _debugCanvas  &&  this . _debugCanvas . getContext ( '2d' ) ; 
@@ -811,6 +842,19 @@ class RenderWebGL extends EventEmitter {
811842        } 
812843    } 
813844
845+     _enterDrawBackground  ( )  { 
846+         const  gl  =  this . gl ; 
847+         const  currentShader  =  this . _shaderManager . getShader ( ShaderManager . DRAW_MODE . background ,  0 ) ; 
848+         gl . disable ( gl . BLEND ) ; 
849+         gl . useProgram ( currentShader . program ) ; 
850+         twgl . setBuffersAndAttributes ( gl ,  currentShader ,  this . _bufferInfo ) ; 
851+     } 
852+ 
853+     _exitDrawBackground  ( )  { 
854+         const  gl  =  this . gl ; 
855+         gl . enable ( gl . BLEND ) ; 
856+     } 
857+ 
814858    _isTouchingColorGpuStart  ( drawableID ,  candidateIDs ,  bounds ,  color3b ,  mask3b )  { 
815859        this . _doExitDrawRegion ( ) ; 
816860
@@ -822,15 +866,8 @@ class RenderWebGL extends EventEmitter {
822866        gl . viewport ( 0 ,  0 ,  bounds . width ,  bounds . height ) ; 
823867        const  projection  =  twgl . m4 . ortho ( bounds . left ,  bounds . right ,  bounds . top ,  bounds . bottom ,  - 1 ,  1 ) ; 
824868
825-         let  fillBackgroundColor  =  this . _backgroundColor ; 
826- 
827-         // When using masking such that the background fill color will showing through, ensure we don't 
828-         // fill using the same color that we are trying to detect! 
829-         if  ( color3b [ 0 ]  >  196  &&  color3b [ 1 ]  >  196  &&  color3b [ 2 ]  >  196 )  { 
830-             fillBackgroundColor  =  [ 0 ,  0 ,  0 ,  255 ] ; 
831-         } 
832- 
833-         gl . clearColor . apply ( gl ,  fillBackgroundColor ) ; 
869+         // Clear the query buffer to fully transparent. This will be the color of pixels that fail the stencil test. 
870+         gl . clearColor ( 0 ,  0 ,  0 ,  0 ) ; 
834871        gl . clear ( gl . COLOR_BUFFER_BIT  |  gl . STENCIL_BUFFER_BIT ) ; 
835872
836873        let  extraUniforms ; 
@@ -842,6 +879,9 @@ class RenderWebGL extends EventEmitter {
842879        } 
843880
844881        try  { 
882+             // Using the stencil buffer, mask out the drawing to either the drawable's alpha channel 
883+             // or pixels of the drawable which match the mask color, depending on whether a mask color is given. 
884+             // Masked-out pixels will not be checked. 
845885            gl . enable ( gl . STENCIL_TEST ) ; 
846886            gl . stencilFunc ( gl . ALWAYS ,  1 ,  1 ) ; 
847887            gl . stencilOp ( gl . KEEP ,  gl . KEEP ,  gl . REPLACE ) ; 
@@ -862,12 +902,25 @@ class RenderWebGL extends EventEmitter {
862902            gl . stencilOp ( gl . KEEP ,  gl . KEEP ,  gl . KEEP ) ; 
863903            gl . colorMask ( true ,  true ,  true ,  true ) ; 
864904
905+             // Draw the background as a quad. Drawing a background with gl.clear will not mask to the stenciled area. 
906+             this . enterDrawRegion ( this . _backgroundDrawRegionId ) ; 
907+ 
908+             const  uniforms  =  { 
909+                 u_backgroundColor : this . _backgroundColor4f 
910+             } ; 
911+ 
912+             const  currentShader  =  this . _shaderManager . getShader ( ShaderManager . DRAW_MODE . background ,  0 ) ; 
913+             twgl . setUniforms ( currentShader ,  uniforms ) ; 
914+             twgl . drawBufferInfo ( gl ,  this . _bufferInfo ,  gl . TRIANGLES ) ; 
915+ 
916+             // Draw the candidate drawables on top of the background. 
865917            this . _drawThese ( candidateIDs ,  ShaderManager . DRAW_MODE . default ,  projection , 
866918                { idFilterFunc : testID  =>  testID  !==  drawableID } 
867919            ) ; 
868920        }  finally  { 
869921            gl . colorMask ( true ,  true ,  true ,  true ) ; 
870922            gl . disable ( gl . STENCIL_TEST ) ; 
923+             this . _doExitDrawRegion ( ) ; 
871924        } 
872925    } 
873926
@@ -886,7 +939,8 @@ class RenderWebGL extends EventEmitter {
886939        } 
887940
888941        for  ( let  pixelBase  =  0 ;  pixelBase  <  pixels . length ;  pixelBase  +=  4 )  { 
889-             if  ( colorMatches ( color3b ,  pixels ,  pixelBase ) )  { 
942+             // Transparent pixels are masked (either by the drawable's alpha channel or color mask). 
943+             if  ( pixels [ pixelBase  +  3 ]  !==  0  &&  colorMatches ( color3b ,  pixels ,  pixelBase ) )  { 
890944                return  true ; 
891945            } 
892946        } 
@@ -1321,7 +1375,7 @@ class RenderWebGL extends EventEmitter {
13211375        gl . viewport ( 0 ,  0 ,  bounds . width ,  bounds . height ) ; 
13221376        const  projection  =  twgl . m4 . ortho ( bounds . left ,  bounds . right ,  bounds . top ,  bounds . bottom ,  - 1 ,  1 ) ; 
13231377
1324-         gl . clearColor . apply ( gl ,  this . _backgroundColor ) ; 
1378+         gl . clearColor . apply ( gl ,  this . _backgroundColor4f ) ; 
13251379        gl . clear ( gl . COLOR_BUFFER_BIT ) ; 
13261380        this . _drawThese ( this . _drawList ,  ShaderManager . DRAW_MODE . default ,  projection ) ; 
13271381
@@ -1411,6 +1465,13 @@ class RenderWebGL extends EventEmitter {
14111465                    // Update the CPU position data 
14121466                    drawable . updateCPURenderAttributes ( ) ; 
14131467                    const  candidateBounds  =  drawable . getFastBounds ( ) ; 
1468+ 
1469+                     // Push bounds out to integers. If a drawable extends out into half a pixel, that half-pixel still 
1470+                     // needs to be tested. Plus, in some areas we construct another rectangle from the union of these, 
1471+                     // and iterate over its pixels (width * height). Turns out that doesn't work so well when the 
1472+                     // width/height aren't integers. 
1473+                     candidateBounds . snapToInt ( ) ; 
1474+ 
14141475                    if  ( bounds . intersects ( candidateBounds ) )  { 
14151476                        result . push ( { 
14161477                            id, 
0 commit comments