Skip to content

Commit 5e08b24

Browse files
committed
Fix touching color check for background color
1 parent d73aeb1 commit 5e08b24

File tree

4 files changed

+87
-19
lines changed

4 files changed

+87
-19
lines changed

src/RenderWebGL.js

Lines changed: 66 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -182,9 +182,23 @@ class RenderWebGL extends EventEmitter {
182182
/** @type {function} */
183183
this._exitRegion = null;
184184

185+
/** @type {object} */
186+
this._backgroundDrawRegionId = {
187+
enter: () => this._enterDrawBackground(),
188+
exit: () => this._exitDrawBackground()
189+
};
190+
185191
/** @type {Array.<snapshotCallback>} */
186192
this._snapshotCallbacks = [];
187193

194+
/** @type {Array<number>}
195+
* @readonly */
196+
this._backgroundColor4f = [0, 0, 0, 0];
197+
198+
/** @type {Uint8ClampedArray}
199+
* @readonly */
200+
this._backgroundColor3b = new Uint8ClampedArray(3);
201+
188202
this._createGeometry();
189203

190204
this.on(RenderConstants.Events.NativeSizeChanged, this.onNativeSizeChanged);
@@ -244,7 +258,12 @@ class RenderWebGL extends EventEmitter {
244258
* @param {number} blue The blue component for the background.
245259
*/
246260
setBackgroundColor (red, green, blue) {
247-
this._backgroundColor = [red, green, blue, 1];
261+
this._backgroundColor4f = [red, green, blue, 1];
262+
263+
this._backgroundColor3b[0] = red * 255;
264+
this._backgroundColor3b[1] = green * 255;
265+
this._backgroundColor3b[2] = blue * 255;
266+
248267
}
249268

250269
/**
@@ -623,7 +642,7 @@ class RenderWebGL extends EventEmitter {
623642

624643
twgl.bindFramebufferInfo(gl, null);
625644
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
626-
gl.clearColor.apply(gl, this._backgroundColor);
645+
gl.clearColor.apply(gl, this._backgroundColor4f);
627646
gl.clear(gl.COLOR_BUFFER_BIT);
628647

629648
this._drawThese(this._drawList, ShaderManager.DRAW_MODE.default, this._projection);
@@ -739,12 +758,20 @@ class RenderWebGL extends EventEmitter {
739758
*/
740759
isTouchingColor (drawableID, color3b, mask3b) {
741760
const candidates = this._candidatesTouching(drawableID, this._visibleDrawList);
742-
if (candidates.length === 0) {
761+
762+
let bounds;
763+
if (colorMatches(color3b, this._backgroundColor3b, 0)) {
764+
// If the color we're checking for is the background color, don't confine the check to
765+
// candidate drawables' bounds--since the background spans the entire stage, we must check
766+
// everything that lies inside the drawable.
767+
bounds = this._touchingBounds(drawableID);
768+
} else if (candidates.length === 0) {
769+
// If not checking for the background color, we can return early if there are no candidate drawables.
743770
return false;
771+
} else {
772+
bounds = this._candidatesBounds(candidates);
744773
}
745774

746-
const bounds = this._candidatesBounds(candidates);
747-
748775
const maxPixelsForCPU = this._getMaxPixelsForCPU();
749776

750777
const debugCanvasContext = this._debugCanvas && this._debugCanvas.getContext('2d');
@@ -805,6 +832,19 @@ class RenderWebGL extends EventEmitter {
805832
}
806833
}
807834

835+
_enterDrawBackground () {
836+
const gl = this.gl;
837+
const currentShader = this._shaderManager.getShader(ShaderManager.DRAW_MODE.background, 0);
838+
gl.disable(gl.BLEND);
839+
gl.useProgram(currentShader.program);
840+
twgl.setBuffersAndAttributes(gl, currentShader, this._bufferInfo);
841+
}
842+
843+
_exitDrawBackground () {
844+
const gl = this.gl;
845+
gl.enable(gl.BLEND);
846+
}
847+
808848
_isTouchingColorGpuStart (drawableID, candidateIDs, bounds, color3b, mask3b) {
809849
this._doExitDrawRegion();
810850

@@ -816,15 +856,8 @@ class RenderWebGL extends EventEmitter {
816856
gl.viewport(0, 0, bounds.width, bounds.height);
817857
const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1);
818858

819-
let fillBackgroundColor = this._backgroundColor;
820-
821-
// When using masking such that the background fill color will showing through, ensure we don't
822-
// fill using the same color that we are trying to detect!
823-
if (color3b[0] > 196 && color3b[1] > 196 && color3b[2] > 196) {
824-
fillBackgroundColor = [0, 0, 0, 255];
825-
}
826-
827-
gl.clearColor.apply(gl, fillBackgroundColor);
859+
// Clear the query buffer to fully transparent. This will be the color of pixels that fail the stencil test.
860+
gl.clearColor(0, 0, 0, 0);
828861
gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
829862

830863
let extraUniforms;
@@ -835,7 +868,11 @@ class RenderWebGL extends EventEmitter {
835868
};
836869
}
837870

871+
// TODO: figure out why this is all in a try block?
838872
try {
873+
// Using the stencil buffer, mask out the drawing to either the drawable's bounds
874+
// or pixels of the drawable which match the mask color, depending on whether a mask color is given.
875+
// Masked-out pixels will not be checked.
839876
gl.enable(gl.STENCIL_TEST);
840877
gl.stencilFunc(gl.ALWAYS, 1, 1);
841878
gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE);
@@ -856,6 +893,18 @@ class RenderWebGL extends EventEmitter {
856893
gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
857894
gl.colorMask(true, true, true, true);
858895

896+
// Draw the background as a quad. Drawing a background with gl.clear will not mask to the stenciled area.
897+
this.enterDrawRegion(this._backgroundDrawRegionId);
898+
899+
const uniforms = {
900+
u_backgroundColor: this._backgroundColor4f
901+
};
902+
903+
const currentShader = this._shaderManager.getShader(ShaderManager.DRAW_MODE.background, 0);
904+
twgl.setUniforms(currentShader, uniforms);
905+
twgl.drawBufferInfo(gl, this._bufferInfo, gl.TRIANGLES);
906+
907+
// Draw the candidate drawables on top of the background.
859908
this._drawThese(candidateIDs, ShaderManager.DRAW_MODE.default, projection,
860909
{idFilterFunc: testID => testID !== drawableID}
861910
);
@@ -880,7 +929,8 @@ class RenderWebGL extends EventEmitter {
880929
}
881930

882931
for (let pixelBase = 0; pixelBase < pixels.length; pixelBase += 4) {
883-
if (colorMatches(color3b, pixels, pixelBase)) {
932+
// Transparent pixels are masked (either by the drawable's bounds or color mask).
933+
if (pixels[pixelBase + 3] !== 0 && colorMatches(color3b, pixels, pixelBase)) {
884934
return true;
885935
}
886936
}
@@ -1210,7 +1260,7 @@ class RenderWebGL extends EventEmitter {
12101260
gl.viewport(0, 0, bounds.width, bounds.height);
12111261
const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1);
12121262

1213-
gl.clearColor.apply(gl, this._backgroundColor);
1263+
gl.clearColor.apply(gl, this._backgroundColor4f);
12141264
gl.clear(gl.COLOR_BUFFER_BIT);
12151265
this._drawThese(this._drawList, ShaderManager.DRAW_MODE.default, projection);
12161266

src/ShaderManager.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,12 @@ ShaderManager.DRAW_MODE = {
176176
/**
177177
* Draw a line with caps.
178178
*/
179-
line: 'line'
179+
line: 'line',
180+
181+
/**
182+
* Draw the background in a certain color. Must sometimes be used instead of gl.clear.
183+
*/
184+
background: 'background'
180185
};
181186

182187
module.exports = ShaderManager;

src/shaders/sprite.frag

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ uniform float u_lineThickness;
3939
uniform vec4 u_penPoints;
4040
#endif // DRAW_MODE_line
4141

42+
#ifdef DRAW_MODE_background
43+
uniform vec4 u_backgroundColor;
44+
#endif // DRAW_MODE_background
45+
4246
uniform sampler2D u_skin;
4347

4448
varying vec2 v_texCoord;
@@ -109,7 +113,7 @@ const vec2 kCenter = vec2(0.5, 0.5);
109113

110114
void main()
111115
{
112-
#ifndef DRAW_MODE_line
116+
#if !(defined(DRAW_MODE_line) || defined(DRAW_MODE_background))
113117
vec2 texcoord0 = v_texCoord;
114118

115119
#ifdef ENABLE_mosaic
@@ -215,7 +219,9 @@ void main()
215219
gl_FragColor.rgb /= gl_FragColor.a + epsilon;
216220
#endif
217221

218-
#else // DRAW_MODE_line
222+
#endif // !(defined(DRAW_MODE_line) || defined(DRAW_MODE_background))
223+
224+
#ifdef DRAW_MODE_line
219225
// Maaaaagic antialiased-line-with-round-caps shader.
220226
// Adapted from Inigo Quilez' 2D distance function cheat sheet
221227
// https://www.iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm
@@ -251,4 +257,8 @@ void main()
251257

252258
gl_FragColor = u_lineColor * cappedLine;
253259
#endif // DRAW_MODE_line
260+
261+
#ifdef DRAW_MODE_background
262+
gl_FragColor = u_backgroundColor;
263+
#endif
254264
}

src/shaders/sprite.vert

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ void main() {
5656

5757
gl_Position = vec4(position, 0, 1);
5858
v_texCoord = position * 0.5 * u_stageSize;
59+
#elif defined(DRAW_MODE_background)
60+
gl_Position = vec4(a_position * 2.0, 0, 1);
61+
v_texCoord = a_texCoord;
5962
#else
6063
gl_Position = u_projectionMatrix * u_modelMatrix * vec4(a_position, 0, 1);
6164
v_texCoord = a_texCoord;

0 commit comments

Comments
 (0)