mapbox-gl
Version:
A WebGL interactive maps library
222 lines (188 loc) • 8.33 kB
JavaScript
'use strict';
var browser = require('../util/browser');
var drawCollisionDebug = require('./draw_collision_debug');
var pixelsToTileUnits = require('../source/pixels_to_tile_units');
module.exports = drawSymbols;
function drawSymbols(painter, sourceCache, layer, coords) {
if (painter.isOpaquePass) return;
var drawAcrossEdges = !(layer.layout['text-allow-overlap'] || layer.layout['icon-allow-overlap'] ||
layer.layout['text-ignore-placement'] || layer.layout['icon-ignore-placement']);
var gl = painter.gl;
// Disable the stencil test so that labels aren't clipped to tile boundaries.
//
// Layers with features that may be drawn overlapping aren't clipped. These
// layers are sorted in the y direction, and to draw the correct ordering near
// tile edges the icons are included in both tiles and clipped when drawing.
if (drawAcrossEdges) {
gl.disable(gl.STENCIL_TEST);
} else {
gl.enable(gl.STENCIL_TEST);
}
drawLayerSymbols(painter, sourceCache, layer, coords, false,
layer.paint['icon-translate'],
layer.paint['icon-translate-anchor'],
layer.layout['icon-rotation-alignment'],
// icon-pitch-alignment is not yet implemented
// and we simply inherit the rotation alignment
layer.layout['icon-rotation-alignment'],
layer.layout['icon-size'],
layer.paint['icon-halo-width'],
layer.paint['icon-halo-color'],
layer.paint['icon-halo-blur'],
layer.paint['icon-opacity'],
layer.paint['icon-color']
);
drawLayerSymbols(painter, sourceCache, layer, coords, true,
layer.paint['text-translate'],
layer.paint['text-translate-anchor'],
layer.layout['text-rotation-alignment'],
layer.layout['text-pitch-alignment'],
layer.layout['text-size'],
layer.paint['text-halo-width'],
layer.paint['text-halo-color'],
layer.paint['text-halo-blur'],
layer.paint['text-opacity'],
layer.paint['text-color']
);
if (sourceCache.map.showCollisionBoxes) {
drawCollisionDebug(painter, sourceCache, layer, coords);
}
}
function drawLayerSymbols(painter, sourceCache, layer, coords, isText,
translate,
translateAnchor,
rotationAlignment,
pitchAlignment,
size,
haloWidth,
haloColor,
haloBlur,
opacity,
color) {
var gl = painter.gl;
painter.setDepthSublayer(0);
painter.depthMask(false);
if (pitchAlignment === 'map') {
gl.enable(gl.DEPTH_TEST);
} else {
gl.disable(gl.DEPTH_TEST);
}
for (var j = 0; j < coords.length; j++) {
var tile = sourceCache.getTile(coords[j]);
var bucket = tile.getBucket(layer);
if (!bucket) continue;
var bothBufferGroups = bucket.bufferGroups;
var bufferGroups = isText ? bothBufferGroups.glyph : bothBufferGroups.icon;
if (!bufferGroups.length) continue;
painter.enableTileClippingMask(coords[j]);
drawSymbol(painter, layer, coords[j].posMatrix, tile, bucket, bufferGroups, isText,
isText || bucket.sdfIcons, !isText && bucket.iconsNeedLinear,
isText ? bucket.adjustedTextSize : bucket.adjustedIconSize, bucket.fontstack,
translate,
translateAnchor,
rotationAlignment,
pitchAlignment,
size,
haloWidth,
haloColor,
haloBlur,
opacity,
color);
}
gl.enable(gl.DEPTH_TEST);
}
function drawSymbol(painter, layer, posMatrix, tile, bucket, bufferGroups, isText, sdf, iconsNeedLinear, adjustedSize, fontstack,
translate,
translateAnchor,
rotationAlignment,
pitchAlignment,
size,
haloWidth,
haloColor,
haloBlur,
opacity,
color) {
var gl = painter.gl;
var tr = painter.transform;
var rotateWithMap = rotationAlignment === 'map';
var pitchWithMap = pitchAlignment === 'map';
var defaultSize = isText ? 24 : 1;
var fontScale = size / defaultSize;
var 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];
}
if (!isText && !painter.style.sprite.loaded())
return;
var 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);
if (isText) {
// use the fonstack used when parsing the tile, not the fontstack
// at the current zoom level (layout['text-font']).
var glyphAtlas = fontstack && painter.glyphSource.getGlyphAtlas(fontstack);
if (!glyphAtlas) return;
glyphAtlas.updateTexture(gl);
gl.uniform2f(program.u_texsize, glyphAtlas.width / 4, glyphAtlas.height / 4);
} else {
var mapMoving = painter.options.rotating || painter.options.zooming;
var iconScaled = fontScale !== 1 || browser.devicePixelRatio !== painter.spriteAtlas.pixelRatio || iconsNeedLinear;
var iconTransformed = pitchWithMap || painter.transform.pitch;
painter.spriteAtlas.bind(gl, sdf || mapMoving || iconScaled || iconTransformed);
gl.uniform2f(program.u_texsize, painter.spriteAtlas.width / 4, painter.spriteAtlas.height / 4);
}
// adjust min/max zooms for variable font sizes
var 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);
var group;
if (sdf) {
var sdfPx = 8;
var blurOffset = 1.19;
var haloOffset = 6;
var gamma = 0.105 * defaultSize / size / browser.devicePixelRatio;
if (haloWidth) {
// Draw halo underneath the text.
gl.uniform1f(program.u_gamma, (haloBlur * blurOffset / fontScale / 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 (var j = 0; j < bufferGroups.length; j++) {
group = bufferGroups[j];
group.vaos[layer.id].bind(gl, program, group.layoutVertexBuffer, group.elementBuffer);
gl.drawElements(gl.TRIANGLES, group.elementBuffer.length * 3, gl.UNSIGNED_SHORT, 0);
}
}
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 (var i = 0; i < bufferGroups.length; i++) {
group = bufferGroups[i];
group.vaos[layer.id].bind(gl, program, group.layoutVertexBuffer, group.elementBuffer);
gl.drawElements(gl.TRIANGLES, group.elementBuffer.length * 3, gl.UNSIGNED_SHORT, 0);
}
} else {
gl.uniform1f(program.u_opacity, opacity);
for (var k = 0; k < bufferGroups.length; k++) {
group = bufferGroups[k];
group.vaos[layer.id].bind(gl, program, group.layoutVertexBuffer, group.elementBuffer);
gl.drawElements(gl.TRIANGLES, group.elementBuffer.length * 3, gl.UNSIGNED_SHORT, 0);
}
}
}