diff --git a/js/render/draw_symbol.js b/js/render/draw_symbol.js index 580637d03e4..78e9ec46994 100644 --- a/js/render/draw_symbol.js +++ b/js/render/draw_symbol.js @@ -4,14 +4,21 @@ const browser = require('../util/browser'); const drawCollisionDebug = require('./draw_collision_debug'); const pixelsToTileUnits = require('../source/pixels_to_tile_units'); - module.exports = drawSymbols; +const sdfPx = 8; +const blurOffset = 1.19; +const haloOffset = 6; +const gamma = 0.105 / browser.devicePixelRatio; + function drawSymbols(painter, sourceCache, layer, coords) { if (painter.isOpaquePass) return; - const drawAcrossEdges = !(layer.layout['text-allow-overlap'] || layer.layout['icon-allow-overlap'] || - layer.layout['text-ignore-placement'] || layer.layout['icon-ignore-placement']); + const drawAcrossEdges = + !layer.layout['text-allow-overlap'] && + !layer.layout['icon-allow-overlap'] && + !layer.layout['text-ignore-placement'] && + !layer.layout['icon-ignore-placement']; const gl = painter.gl; @@ -26,6 +33,9 @@ function drawSymbols(painter, sourceCache, layer, coords) { gl.enable(gl.STENCIL_TEST); } + painter.setDepthSublayer(0); + painter.depthMask(false); + drawLayerSymbols(painter, sourceCache, layer, coords, false, layer.paint['icon-translate'], layer.paint['icon-translate-anchor'], @@ -59,92 +69,59 @@ function drawSymbols(painter, sourceCache, layer, coords) { } } -function drawLayerSymbols(painter, sourceCache, layer, coords, isText, - translate, - translateAnchor, - rotationAlignment, - pitchAlignment, - size, - haloWidth, - haloColor, - haloBlur, - opacity, - color) { +function drawLayerSymbols(painter, sourceCache, layer, coords, isText, translate, translateAnchor, + rotationAlignment, pitchAlignment, size, haloWidth, haloColor, haloBlur, opacity, color) { + + if (!isText && painter.style.sprite && !painter.style.sprite.loaded()) + return; const gl = painter.gl; - painter.setDepthSublayer(0); - painter.depthMask(false); - if (pitchAlignment === 'map') { + + const rotateWithMap = rotationAlignment === 'map'; + const pitchWithMap = pitchAlignment === 'map'; + + if (pitchWithMap) { gl.enable(gl.DEPTH_TEST); } else { gl.disable(gl.DEPTH_TEST); } - for (let j = 0; j < coords.length; j++) { - const tile = sourceCache.getTile(coords[j]); + let program; + + for (const coord of coords) { + const tile = sourceCache.getTile(coord); const bucket = tile.getBucket(layer); if (!bucket) continue; const buffers = isText ? bucket.buffers.glyph : bucket.buffers.icon; if (!buffers.segments.length) continue; - painter.enableTileClippingMask(coords[j]); - drawSymbol(painter, layer, coords[j].posMatrix, tile, bucket, buffers, isText, - isText || bucket.sdfIcons, !isText && bucket.iconsNeedLinear, - isText ? bucket.adjustedTextSize : bucket.adjustedIconSize, bucket.fontstack, - translate, - translateAnchor, - rotationAlignment, - pitchAlignment, - size, - haloWidth, - haloColor, - haloBlur, - opacity, - color); - } + const isSDF = isText || bucket.sdfIcons; - gl.enable(gl.DEPTH_TEST); -} + if (!program) { + program = painter.useProgram(isSDF ? 'symbolSDF' : 'symbolIcon'); -function drawSymbol(painter, layer, posMatrix, tile, bucket, buffers, isText, sdf, iconsNeedLinear, adjustedSize, fontstack, - translate, - translateAnchor, - rotationAlignment, - pitchAlignment, - size, - haloWidth, - haloColor, - haloBlur, - opacity, - color) { + setSymbolDrawState(program, painter, isText, isSDF, rotateWithMap, pitchWithMap, bucket.fontstack, size, + bucket.iconsNeedLinear, isText ? bucket.adjustedTextSize : bucket.adjustedIconSize, opacity); + } - const gl = painter.gl; - const tr = painter.transform; - const rotateWithMap = rotationAlignment === 'map'; - const pitchWithMap = pitchAlignment === 'map'; + painter.enableTileClippingMask(coord); - const defaultSize = isText ? 24 : 1; - const fontScale = size / defaultSize; + gl.uniformMatrix4fv(program.u_matrix, false, + painter.translatePosMatrix(coord.posMatrix, tile, translate, translateAnchor)); - let extrudeScale, s, gammaScale; - if (pitchWithMap) { - s = pixelsToTileUnits(tile, 1, painter.transform.zoom) * fontScale; - gammaScale = 1 / Math.cos(tr._pitch); - extrudeScale = [s, s]; - } else { - s = painter.transform.altitude * fontScale; - gammaScale = 1; - extrudeScale = [ tr.pixelsToGLUnits[0] * s, tr.pixelsToGLUnits[1] * s]; + drawTileSymbols(program, painter, layer, tile, buffers, isText, isSDF, + pitchWithMap, size, haloWidth, haloColor, haloBlur, color); } +} - if (!isText && !painter.style.sprite.loaded()) - return; +function setSymbolDrawState(program, painter, isText, isSDF, rotateWithMap, pitchWithMap, fontstack, size, + iconsNeedLinear, adjustedSize, opacity) { + + const gl = painter.gl; + const tr = painter.transform; - const program = painter.useProgram(sdf ? 'symbolSDF' : 'symbolIcon'); - gl.uniformMatrix4fv(program.u_matrix, false, painter.translatePosMatrix(posMatrix, tile, translate, translateAnchor)); gl.uniform1i(program.u_rotate_with_map, rotateWithMap); gl.uniform1i(program.u_pitch_with_map, pitchWithMap); - gl.uniform2fv(program.u_extrude_scale, extrudeScale); gl.activeTexture(gl.TEXTURE0); gl.uniform1i(program.u_texture, 0); @@ -159,57 +136,65 @@ function drawSymbol(painter, layer, posMatrix, tile, bucket, buffers, isText, sd gl.uniform2f(program.u_texsize, glyphAtlas.width / 4, glyphAtlas.height / 4); } else { const mapMoving = painter.options.rotating || painter.options.zooming; - const iconScaled = fontScale !== 1 || browser.devicePixelRatio !== painter.spriteAtlas.pixelRatio || iconsNeedLinear; - const iconTransformed = pitchWithMap || painter.transform.pitch; - painter.spriteAtlas.bind(gl, sdf || mapMoving || iconScaled || iconTransformed); + const iconScaled = size !== 1 || browser.devicePixelRatio !== painter.spriteAtlas.pixelRatio || iconsNeedLinear; + const iconTransformed = pitchWithMap || tr.pitch; + painter.spriteAtlas.bind(gl, isSDF || mapMoving || iconScaled || iconTransformed); gl.uniform2f(program.u_texsize, painter.spriteAtlas.width / 4, painter.spriteAtlas.height / 4); } - // adjust min/max zooms for variable font sizes - const zoomAdjust = Math.log(size / adjustedSize) / Math.LN2 || 0; - gl.uniform1f(program.u_zoom, (painter.transform.zoom - zoomAdjust) * 10); // current zoom level - gl.activeTexture(gl.TEXTURE1); painter.frameHistory.bind(gl); gl.uniform1i(program.u_fadetexture, 1); - if (sdf) { - const sdfPx = 8; - const blurOffset = 1.19; - const haloOffset = 6; - const gamma = 0.105 * defaultSize / size / browser.devicePixelRatio; + // adjust min/max zooms for variable font sizes + const zoomAdjust = Math.log(size / adjustedSize) / Math.LN2 || 0; + gl.uniform1f(program.u_zoom, (tr.zoom - zoomAdjust) * 10); // current zoom level + + gl.uniform1f(program.u_pitch, tr.pitch / 360 * 2 * Math.PI); + gl.uniform1f(program.u_bearing, tr.bearing / 360 * 2 * Math.PI); + gl.uniform1f(program.u_aspect_ratio, tr.width / tr.height); + + gl.uniform1f(program.u_opacity, opacity); +} + +function drawTileSymbols(program, painter, layer, tile, buffers, isText, isSDF, + pitchWithMap, size, haloWidth, haloColor, haloBlur, color) { + + const gl = painter.gl; + const tr = painter.transform; + + const fontScale = size / (isText ? 24 : 1); + + if (pitchWithMap) { + const s = pixelsToTileUnits(tile, fontScale, tr.zoom); + gl.uniform2f(program.u_extrude_scale, s, s); + } else { + const s = tr.altitude * fontScale; + gl.uniform2f(program.u_extrude_scale, tr.pixelsToGLUnits[0] * s, tr.pixelsToGLUnits[1] * s); + } - if (haloWidth) { - // Draw halo underneath the text. - gl.uniform1f(program.u_gamma, (haloBlur * blurOffset / fontScale / sdfPx + gamma) * gammaScale); + if (isSDF) { + const gammaScale = fontScale * (pitchWithMap ? Math.cos(tr._pitch) : 1); + + if (haloWidth) { // Draw halo underneath the text. + gl.uniform1f(program.u_gamma, (haloBlur * blurOffset / sdfPx + gamma) / gammaScale); gl.uniform4fv(program.u_color, haloColor); - gl.uniform1f(program.u_opacity, opacity); gl.uniform1f(program.u_buffer, (haloOffset - haloWidth / fontScale) / sdfPx); - for (const segment of buffers.segments) { - segment.vaos[layer.id].bind(gl, program, buffers.layoutVertexBuffer, buffers.elementBuffer, null, segment.vertexOffset); - gl.drawElements(gl.TRIANGLES, segment.primitiveLength * 3, gl.UNSIGNED_SHORT, segment.primitiveOffset * 3 * 2); - } + drawSymbolElements(buffers, layer, gl, program); } - gl.uniform1f(program.u_gamma, gamma * gammaScale); + gl.uniform1f(program.u_gamma, gamma / gammaScale); gl.uniform4fv(program.u_color, color); - gl.uniform1f(program.u_opacity, opacity); gl.uniform1f(program.u_buffer, (256 - 64) / 256); - gl.uniform1f(program.u_pitch, tr.pitch / 360 * 2 * Math.PI); - gl.uniform1f(program.u_bearing, tr.bearing / 360 * 2 * Math.PI); - gl.uniform1f(program.u_aspect_ratio, tr.width / tr.height); + } - for (const segment of buffers.segments) { - segment.vaos[layer.id].bind(gl, program, buffers.layoutVertexBuffer, buffers.elementBuffer, null, segment.vertexOffset); - gl.drawElements(gl.TRIANGLES, segment.primitiveLength * 3, gl.UNSIGNED_SHORT, segment.primitiveOffset * 3 * 2); - } + drawSymbolElements(buffers, layer, gl, program); +} - } else { - gl.uniform1f(program.u_opacity, opacity); - for (const segment of buffers.segments) { - segment.vaos[layer.id].bind(gl, program, buffers.layoutVertexBuffer, buffers.elementBuffer, null, segment.vertexOffset); - gl.drawElements(gl.TRIANGLES, segment.primitiveLength * 3, gl.UNSIGNED_SHORT, segment.primitiveOffset * 3 * 2); - } +function drawSymbolElements(buffers, layer, gl, program) { + for (const segment of buffers.segments) { + segment.vaos[layer.id].bind(gl, program, buffers.layoutVertexBuffer, buffers.elementBuffer, null, segment.vertexOffset); + gl.drawElements(gl.TRIANGLES, segment.primitiveLength * 3, gl.UNSIGNED_SHORT, segment.primitiveOffset * 3 * 2); } }