Skip to content

Commit

Permalink
Enable property functions for text-size
Browse files Browse the repository at this point in the history
  • Loading branch information
Anand Thakker committed Mar 20, 2017
1 parent c53aed4 commit 8f8e4b3
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 48 deletions.
68 changes: 65 additions & 3 deletions debug/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,74 @@
<script src='/dist/mapbox-gl-dev.js'></script>
<script src='/debug/access_token_generated.js'></script>
<script>
var style = {
"version": 8,
"metadata": {
"test": {
"width": 64,
"height": 64
}
},
"sources": {
"geojson": {
"type": "geojson",
"data": {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": { "x": 0 },
"geometry": {
"type": "Point",
"coordinates": [ -10, 0 ]
}
},
{
"type": "Feature",
"properties": { "x": 5 },
"geometry": {
"type": "Point",
"coordinates": [
10,
0
]
}
}
]
}
}
},
"glyphs": "mapbox://fonts/mapbox/{fontstack}/{range}.pbf",
"layers": [
{
"id": "symbol",
"type": "symbol",
"source": "geojson",
"layout": {
"text-field": "A",
"text-font": [
"Open Sans Semibold",
"Arial Unicode MS Bold"
],
"text-size": {
"property": "x",
"stops": [
[ { "value": 0, "zoom": 0 }, 12 ],
[ { "value": 10, "zoom": 0 }, 24 ],
[ { "value": 0, "zoom": 4 }, 48 ],
[ { "value": 10, "zoom": 4 }, 96 ]
]
}
}
}
]
};

var map = window.map = new mapboxgl.Map({
container: 'map',
zoom: 12.5,
center: [-77.01866, 38.888],
style: 'mapbox://styles/mapbox/streets-v10',
zoom: 0,
center: [0, 0],
style: style,
hash: true
});

Expand Down
29 changes: 13 additions & 16 deletions src/data/bucket/symbol_bucket.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const symbolInterfaces = {
layoutVertexArrayType: layoutVertexArrayType,
elementArrayType: elementArrayType,
paintAttributes: [
{name: 'a_size', property: 'text-size', type: 'Uint16', multiplier: 10},
{name: 'a_fill_color', property: 'text-color', type: 'Uint8'},
{name: 'a_halo_color', property: 'text-halo-color', type: 'Uint8'},
{name: 'a_halo_width', property: 'text-halo-width', type: 'Uint16', multiplier: 10},
Expand All @@ -53,6 +54,7 @@ const symbolInterfaces = {
layoutVertexArrayType: layoutVertexArrayType,
elementArrayType: elementArrayType,
paintAttributes: [
{name: 'a_size', property: 'icon-size', type: 'Uint16', multiplier: 10},
{name: 'a_fill_color', property: 'icon-color', type: 'Uint8'},
{name: 'a_halo_color', property: 'icon-halo-color', type: 'Uint8'},
{name: 'a_halo_width', property: 'icon-halo-width', type: 'Uint16', multiplier: 10},
Expand Down Expand Up @@ -138,8 +140,6 @@ class SymbolBucket {
this.index = options.index;
this.sdfIcons = options.sdfIcons;
this.iconsNeedLinear = options.iconsNeedLinear;
this.adjustedTextSize = options.adjustedTextSize;
this.adjustedIconSize = options.adjustedIconSize;
this.fontstack = options.fontstack;

if (options.arrays) {
Expand Down Expand Up @@ -245,8 +245,6 @@ class SymbolBucket {
layerIds: this.layers.map((l) => l.id),
sdfIcons: this.sdfIcons,
iconsNeedLinear: this.iconsNeedLinear,
adjustedTextSize: this.adjustedTextSize,
adjustedIconSize: this.adjustedIconSize,
fontstack: this.fontstack,
arrays: util.mapObject(this.arrays, (a) => a.isEmpty() ? null : a.serialize(transferables))
};
Expand All @@ -270,15 +268,6 @@ class SymbolBucket {
prepare(stacks, icons) {
this.symbolInstances = [];

// To reduce the number of labels that jump around when zooming we need
// to use a text-size value that is the same for all zoom levels.
// This calculates text-size at a high zoom level so that all tiles can
// use the same value when calculating anchor positions.
this.adjustedTextMaxSize = this.layers[0].getLayoutValue('text-size', {zoom: 18});
this.adjustedTextSize = this.layers[0].getLayoutValue('text-size', {zoom: this.zoom + 1});
this.adjustedIconMaxSize = this.layers[0].getLayoutValue('icon-size', {zoom: 18});
this.adjustedIconSize = this.layers[0].getLayoutValue('icon-size', {zoom: this.zoom + 1});

const tileSize = 512 * this.overscaling;
this.tilePixelRatio = EXTENT / tileSize;
this.compareText = {};
Expand Down Expand Up @@ -368,13 +357,21 @@ class SymbolBucket {
}

addFeature(feature, shapedTextOrientations, shapedIcon) {
// To reduce the number of labels that jump around when zooming we need
// to use a text-size value that is the same for all zoom levels.
// This calculates text-size at a high zoom level so that all tiles can
// use the same value when calculating anchor positions.
const adjustedTextSize = this.layers[0].getLayoutValue('text-size', {zoom: this.zoom + 1}, feature.properties);
const adjustedIconSize = this.layers[0].getLayoutValue('text-size', {zoom: this.zoom + 1}, feature.properties);
const adjustedTextMaxSize = this.layers[0].getLayoutValue('text-size', {zoom: 18}, feature.properties);

const layout = this.layers[0].layout,
glyphSize = 24,
fontScale = this.adjustedTextSize / glyphSize,
textMaxSize = this.adjustedTextMaxSize !== undefined ? this.adjustedTextMaxSize : this.adjustedTextSize,
fontScale = adjustedTextSize / glyphSize,
textMaxSize = adjustedTextMaxSize !== undefined ? adjustedTextMaxSize : adjustedTextSize,
textBoxScale = this.tilePixelRatio * fontScale,
textMaxBoxScale = this.tilePixelRatio * textMaxSize / glyphSize,
iconBoxScale = this.tilePixelRatio * this.adjustedIconSize,
iconBoxScale = this.tilePixelRatio * adjustedIconSize,
symbolMinDistance = this.tilePixelRatio * layout['symbol-spacing'],
avoidEdges = layout['symbol-avoid-edges'],
textPadding = layout['text-padding'] * this.tilePixelRatio,
Expand Down
56 changes: 36 additions & 20 deletions src/render/draw_symbol.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,14 @@ function drawSymbols(painter, sourceCache, layer, coords) {
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.layout['icon-rotation-alignment']
);

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.layout['text-pitch-alignment']
);

if (sourceCache.map.showCollisionBoxes) {
Expand All @@ -55,7 +53,7 @@ function drawSymbols(painter, sourceCache, layer, coords) {
}

function drawLayerSymbols(painter, sourceCache, layer, coords, isText, translate, translateAnchor,
rotationAlignment, pitchAlignment, size) {
rotationAlignment, pitchAlignment) {

if (!isText && painter.style.sprite && !painter.style.sprite.loaded())
return;
Expand Down Expand Up @@ -90,8 +88,7 @@ function drawLayerSymbols(painter, sourceCache, layer, coords, isText, translate
program = painter.useProgram(isSDF ? 'symbolSDF' : 'symbolIcon', programConfiguration);
programConfiguration.setUniforms(gl, program, layer, {zoom: painter.transform.zoom});

setSymbolDrawState(program, painter, isText, isSDF, rotateWithMap, pitchWithMap, bucket.fontstack, size,
bucket.iconsNeedLinear, isText ? bucket.adjustedTextSize : bucket.adjustedIconSize);
setSymbolDrawState(program, painter, layer, coord.z, isText, isSDF, rotateWithMap, pitchWithMap, bucket.fontstack, bucket.iconsNeedLinear);
}

painter.enableTileClippingMask(coord);
Expand All @@ -100,16 +97,15 @@ function drawLayerSymbols(painter, sourceCache, layer, coords, isText, translate
painter.translatePosMatrix(coord.posMatrix, tile, translate, translateAnchor));

drawTileSymbols(program, painter, layer, tile, buffers, isText, isSDF,
pitchWithMap, size);
pitchWithMap);

prevFontstack = bucket.fontstack;
}

if (!depthOn) gl.enable(gl.DEPTH_TEST);
}

function setSymbolDrawState(program, painter, isText, isSDF, rotateWithMap, pitchWithMap, fontstack, size,
iconsNeedLinear, adjustedSize) {
function setSymbolDrawState(program, painter, layer, tileZoom, isText, isSDF, rotateWithMap, pitchWithMap, fontstack, iconsNeedLinear) {

const gl = painter.gl;
const tr = painter.transform;
Expand All @@ -120,6 +116,8 @@ function setSymbolDrawState(program, painter, isText, isSDF, rotateWithMap, pitc
gl.activeTexture(gl.TEXTURE0);
gl.uniform1i(program.u_texture, 0);

gl.uniform1f(program.u_is_text, isText ? 1 : 0);

if (isText) {
// use the fonstack used when parsing the tile, not the fontstack
// at the current zoom level (layout['text-font']).
Expand All @@ -130,7 +128,10 @@ function setSymbolDrawState(program, painter, isText, isSDF, rotateWithMap, pitc
gl.uniform2f(program.u_texsize, glyphAtlas.width / 4, glyphAtlas.height / 4);
} else {
const mapMoving = painter.options.rotating || painter.options.zooming;
const iconScaled = size !== 1 || browser.devicePixelRatio !== painter.spriteAtlas.pixelRatio || iconsNeedLinear;
const iconSizeScaled = !layer.isLayoutValueFeatureConstant('text-size') ||
!layer.isLayoutValueZoomConstant('text-size') ||
layer.getLayoutValue('text-size', { zoom: tr.zoom }, {}) !== 1;
const iconScaled = iconSizeScaled || 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);
Expand All @@ -141,35 +142,50 @@ function setSymbolDrawState(program, painter, isText, isSDF, rotateWithMap, pitc
gl.uniform1i(program.u_fadetexture, 1);

// 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_zoom, tr.zoom);

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);
}

function drawTileSymbols(program, painter, layer, tile, buffers, isText, isSDF,
pitchWithMap, size) {
pitchWithMap) {

const gl = painter.gl;
const tr = painter.transform;

const fontScale = size / (isText ? 24 : 1);
// If {text,icon}-size is a composite function, the shader needs to
// evaluate it at the zoom level that was used at *layout* time, which
// is distinct the current *rendered* zoom level.
const sizeProperty = isText ? 'text-size' : 'icon-size';
const isSizeCompositeFunction =
!layer.isLayoutValueZoomConstant(sizeProperty) &&
!layer.isLayoutValueFeatureConstant(sizeProperty);
gl.uniform1f(program.u_is_size_composite, isSizeCompositeFunction ? 1 : 0);
if (isSizeCompositeFunction) {
gl.uniform1f(program.u_adjusted_size_t,
layer.getLayoutInterpolationT(sizeProperty, { zoom: tile.coord.z }));
} else {
gl.uniform1f(program.u_adjusted_size,
layer.getLayoutValue(sizeProperty, { zoom: tile.coord.z })
);
}

if (pitchWithMap) {
const s = pixelsToTileUnits(tile, fontScale, tr.zoom);
const s = pixelsToTileUnits(tile, 1, tr.zoom);
gl.uniform2f(program.u_extrude_scale, s, s);
} else {
const s = tr.cameraToCenterDistance * fontScale;
gl.uniform2f(program.u_extrude_scale, tr.pixelsToGLUnits[0] * s, tr.pixelsToGLUnits[1] * s);
const s = tr.cameraToCenterDistance;
gl.uniform2f(program.u_extrude_scale,
tr.pixelsToGLUnits[0] * s,
tr.pixelsToGLUnits[1] * s);
}

if (isSDF) {
const haloWidthProperty = `${isText ? 'text' : 'icon'}-halo-width`;
const hasHalo = !layer.isPaintValueFeatureConstant(haloWidthProperty) || layer.paint[haloWidthProperty];
const gammaScale = fontScale * (pitchWithMap ? Math.cos(tr._pitch) : 1) * tr.cameraToCenterDistance;
gl.uniform1f(program.u_font_scale, fontScale);
const gammaScale = (isText ? 1 / 24 : 1) * (pitchWithMap ? Math.cos(tr._pitch) : 1) * tr.cameraToCenterDistance;
gl.uniform1f(program.u_gamma_scale, gammaScale);

if (hasHalo) { // Draw halo underneath the text.
Expand Down
12 changes: 8 additions & 4 deletions src/shaders/symbol_sdf.fragment.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define EDGE_GAMMA 0.105/DEVICE_PIXEL_RATIO

uniform bool u_is_halo;
#pragma mapbox: define mediump float size
#pragma mapbox: define lowp vec4 fill_color
#pragma mapbox: define lowp vec4 halo_color
#pragma mapbox: define lowp float opacity
Expand All @@ -10,27 +11,30 @@ uniform bool u_is_halo;

uniform sampler2D u_texture;
uniform sampler2D u_fadetexture;
uniform lowp float u_font_scale;
uniform highp float u_gamma_scale;
uniform bool u_is_text;

varying vec2 v_tex;
varying vec2 v_fade_tex;
varying float v_gamma_scale;

void main() {
#pragma mapbox: initialize mediump float size
#pragma mapbox: initialize lowp vec4 fill_color
#pragma mapbox: initialize lowp vec4 halo_color
#pragma mapbox: initialize lowp float opacity
#pragma mapbox: initialize lowp float halo_width
#pragma mapbox: initialize lowp float halo_blur

float fontScale = u_is_text ? size / 24.0 : size;

lowp vec4 color = fill_color;
lowp float gamma = EDGE_GAMMA / u_gamma_scale;
lowp float gamma = EDGE_GAMMA / (size * u_gamma_scale);
lowp float buff = (256.0 - 64.0) / 256.0;
if (u_is_halo) {
color = halo_color;
gamma = (halo_blur * 1.19 / SDF_PX + EDGE_GAMMA) / u_gamma_scale;
buff = (6.0 - halo_width / u_font_scale) / SDF_PX;
gamma = (halo_blur * 1.19 / SDF_PX + EDGE_GAMMA) / (size * u_gamma_scale);
buff = (6.0 - halo_width / fontScale) / SDF_PX;
}

lowp float dist = texture2D(u_texture, v_tex).a;
Expand Down
Loading

0 comments on commit 8f8e4b3

Please sign in to comment.