Skip to content

Commit

Permalink
implement stretchable icons
Browse files Browse the repository at this point in the history
  • Loading branch information
ansis committed Nov 26, 2019
1 parent 63e86f5 commit 9f01202
Show file tree
Hide file tree
Showing 30 changed files with 1,814 additions and 66 deletions.
129 changes: 129 additions & 0 deletions debug/stretchable.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
<!DOCTYPE html>
<html>
<head>
<title>Mapbox GL JS debug page</title>
<meta charset='utf-8'>
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<link rel='stylesheet' href='../dist/mapbox-gl.css' />
<style>
body { margin: 0; padding: 0; }
html, body, #map { height: 100%; }
</style>
</head>

<body>
<div id='map'></div>

<script src='../dist/mapbox-gl-dev.js'></script>
<script src='../debug/access_token_generated.js'></script>
<script>

var map = window.map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v10',
hash: true
});

const border = 16;

map.on('styleimagemissing', function(e) {
var id = e.id; // id of the missing image

// check if this missing icon is one this function can generate
var prefix = 'stretchable-';
if (id.indexOf(prefix) !== 0) return;

// extract the color from the id
var rgb1 = id.replace(prefix, '').split(',').map(Number);

var width = 64; // The image will be 64 pixels square
var bytesPerPixel = 4; // Each pixel is represented by 4 bytes: red, green, blue, and alpha.
var data = new Uint8Array(width * width * bytesPerPixel);

const rgb2 = [rgb1[2], rgb1[1], rgb1[0]];

for (var x = 0; x < width; x++) {
for (var y = 0; y < width; y++) {
var offset = (y * width + x) * bytesPerPixel;

let rgb =
x < border || x + border > width - 1 ||
y < border || y + border > width - 1 ?
rgb2 :
rgb1;

const half = width / 2;
if (Math.abs(x - half) + Math.abs(y - half) < border / 2) {
rgb = [255, 255, 255];
}

data[offset + 0] = rgb[0]; // red
data[offset + 1] = rgb[1]; // green
data[offset + 2] = rgb[2]; // blue
data[offset + 3] = 255; // alpha
}
}

map.addImage(id, {width, height: width, data}, {
content: [border, border, width - border, width - border],
stretchX: [[border, (width - border) / 2], [(width + border) / 2, width - border]],
stretchY: [[border, (width - border) / 2], [(width + border) / 2, width - border]]
});
});

map.on('load', function () {
map.addLayer({
"id": "points",
"type": "symbol",
"source": {
"type": "geojson",
"data": {
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [0, 0]
},
"properties": {
"text": "ASDF",
"size": 36,
"color": "255,0,0"
}
}, {
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [50, 0]
},
"properties": {
"text": "ASDF \n - \nqwer",
"size": 24,
"color": "255,209,28"
}
}, {
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-50, 0]
},
"properties": {
"text": "ASDF",
"size": 18,
"color": "242,127,32"
}
}]
}
},
"layout": {
"text-field": ["get", "text"],
"icon-text-fit": "both",
"text-size": ["get", "size"],
"icon-image": ["concat", "stretchable-", ["get", "color"]]
}
});
});

</script>
</body>
</html>
23 changes: 14 additions & 9 deletions src/data/array_types.js
Original file line number Diff line number Diff line change
Expand Up @@ -189,10 +189,11 @@ register('StructArrayLayout8ui16', StructArrayLayout8ui16);
* Implementation of the StructArray layout:
* [0]: Int16[4]
* [8]: Uint16[4]
* [16]: Int16[4]
*
* @private
*/
class StructArrayLayout4i4ui16 extends StructArray {
class StructArrayLayout4i4ui4i24 extends StructArray {
uint8: Uint8Array;
int16: Int16Array;
uint16: Uint16Array;
Expand All @@ -203,14 +204,14 @@ class StructArrayLayout4i4ui16 extends StructArray {
this.uint16 = new Uint16Array(this.arrayBuffer);
}

emplaceBack(v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number) {
emplaceBack(v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number, v8: number, v9: number, v10: number, v11: number) {
const i = this.length;
this.resize(i + 1);
return this.emplace(i, v0, v1, v2, v3, v4, v5, v6, v7);
return this.emplace(i, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11);
}

emplace(i: number, v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number) {
const o2 = i * 8;
emplace(i: number, v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number, v8: number, v9: number, v10: number, v11: number) {
const o2 = i * 12;
this.int16[o2 + 0] = v0;
this.int16[o2 + 1] = v1;
this.int16[o2 + 2] = v2;
Expand All @@ -219,12 +220,16 @@ class StructArrayLayout4i4ui16 extends StructArray {
this.uint16[o2 + 5] = v5;
this.uint16[o2 + 6] = v6;
this.uint16[o2 + 7] = v7;
this.int16[o2 + 8] = v8;
this.int16[o2 + 9] = v9;
this.int16[o2 + 10] = v10;
this.int16[o2 + 11] = v11;
return i;
}
}

StructArrayLayout4i4ui16.prototype.bytesPerElement = 16;
register('StructArrayLayout4i4ui16', StructArrayLayout4i4ui16);
StructArrayLayout4i4ui4i24.prototype.bytesPerElement = 24;
register('StructArrayLayout4i4ui4i24', StructArrayLayout4i4ui4i24);

/**
* Implementation of the StructArray layout:
Expand Down Expand Up @@ -1143,7 +1148,7 @@ export {
StructArrayLayout2i4i12,
StructArrayLayout2i4ub8,
StructArrayLayout8ui16,
StructArrayLayout4i4ui16,
StructArrayLayout4i4ui4i24,
StructArrayLayout3f12,
StructArrayLayout1ul4,
StructArrayLayout6i1ul2ui2i24,
Expand All @@ -1167,7 +1172,7 @@ export {
StructArrayLayout2i4 as HeatmapLayoutArray,
StructArrayLayout2i4ub8 as LineLayoutArray,
StructArrayLayout8ui16 as PatternLayoutArray,
StructArrayLayout4i4ui16 as SymbolLayoutArray,
StructArrayLayout4i4ui4i24 as SymbolLayoutArray,
StructArrayLayout3f12 as SymbolDynamicLayoutArray,
StructArrayLayout1ul4 as SymbolOpacityArray,
StructArrayLayout2i2i2i12 as CollisionBoxLayoutArray,
Expand Down
5 changes: 3 additions & 2 deletions src/data/bucket/symbol_attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import {createLayout} from '../../util/struct_array';

export const symbolLayoutAttributes = createLayout([
{name: 'a_pos_offset', components: 4, type: 'Int16'},
{name: 'a_data', components: 4, type: 'Uint16'}
]);
{name: 'a_data', components: 4, type: 'Uint16'},
{name: 'a_pixeloffset', components: 4, type: 'Int16'}
], 4);

export const dynamicLayoutAttributes = createLayout([
{name: 'a_projected_pos', components: 3, type: 'Float32'}
Expand Down
22 changes: 15 additions & 7 deletions src/data/bucket/symbol_bucket.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ const shaderOpacityAttributes = [
{name: 'a_fade_opacity', components: 1, type: 'Uint8', offset: 0}
];

function addVertex(array, anchorX, anchorY, ox, oy, tx, ty, sizeVertex, isSDF: boolean) {
function addVertex(array, anchorX, anchorY, ox, oy, tx, ty, sizeVertex, isSDF: boolean, pixelOffsetX, pixelOffsetY, minFontScaleX, minFontScaleY) {
const aSizeX = sizeVertex ? Math.min(MAX_PACKED_SIZE, Math.round(sizeVertex[0])) : 0;
const aSizeY = sizeVertex ? Math.min(MAX_PACKED_SIZE, Math.round(sizeVertex[1])) : 0;
array.emplaceBack(
Expand All @@ -116,7 +116,11 @@ function addVertex(array, anchorX, anchorY, ox, oy, tx, ty, sizeVertex, isSDF: b
tx, // x coordinate of symbol on glyph atlas texture
ty, // y coordinate of symbol on glyph atlas texture
(aSizeX << 1) + (isSDF ? 1 : 0),
aSizeY
aSizeY,
pixelOffsetX * 16,
pixelOffsetY * 16,
minFontScaleX * 256,
minFontScaleY * 256
);
}

Expand Down Expand Up @@ -593,15 +597,19 @@ class SymbolBucket implements Bucket {
tr = symbol.tr,
bl = symbol.bl,
br = symbol.br,
tex = symbol.tex;
tex = symbol.tex,
pixelOffsetTL = symbol.pixelOffsetTL,
pixelOffsetBR = symbol.pixelOffsetBR,
mfsx = symbol.minFontScaleX,
mfsy = symbol.minFontScaleY;

const index = segment.vertexLength;

const y = symbol.glyphOffset[1];
addVertex(layoutVertexArray, labelAnchor.x, labelAnchor.y, tl.x, y + tl.y, tex.x, tex.y, sizeVertex, symbol.isSDF);
addVertex(layoutVertexArray, labelAnchor.x, labelAnchor.y, tr.x, y + tr.y, tex.x + tex.w, tex.y, sizeVertex, symbol.isSDF);
addVertex(layoutVertexArray, labelAnchor.x, labelAnchor.y, bl.x, y + bl.y, tex.x, tex.y + tex.h, sizeVertex, symbol.isSDF);
addVertex(layoutVertexArray, labelAnchor.x, labelAnchor.y, br.x, y + br.y, tex.x + tex.w, tex.y + tex.h, sizeVertex, symbol.isSDF);
addVertex(layoutVertexArray, labelAnchor.x, labelAnchor.y, tl.x, y + tl.y, tex.x, tex.y, sizeVertex, symbol.isSDF, pixelOffsetTL.x, pixelOffsetTL.y, mfsx, mfsy);
addVertex(layoutVertexArray, labelAnchor.x, labelAnchor.y, tr.x, y + tr.y, tex.x + tex.w, tex.y, sizeVertex, symbol.isSDF, pixelOffsetBR.x, pixelOffsetTL.y, mfsx, mfsy);
addVertex(layoutVertexArray, labelAnchor.x, labelAnchor.y, bl.x, y + bl.y, tex.x, tex.y + tex.h, sizeVertex, symbol.isSDF, pixelOffsetTL.x, pixelOffsetBR.y, mfsx, mfsy);
addVertex(layoutVertexArray, labelAnchor.x, labelAnchor.y, br.x, y + br.y, tex.x + tex.w, tex.y + tex.h, sizeVertex, symbol.isSDF, pixelOffsetBR.x, pixelOffsetBR.y, mfsx, mfsy);

addDynamicAttributes(dynamicLayoutVertexArray, labelAnchor, angle);

Expand Down
8 changes: 7 additions & 1 deletion src/render/image_atlas.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,16 @@ export class ImagePosition {
paddedRect: Rect;
pixelRatio: number;
version: number;
stretchY: Array<Array<number>> | void;
stretchX: Array<Array<number>> | void;
content: Array<number> | void;

constructor(paddedRect: Rect, {pixelRatio, version}: StyleImage) {
constructor(paddedRect: Rect, {pixelRatio, version, stretchX, stretchY, content}: StyleImage) {
this.paddedRect = paddedRect;
this.pixelRatio = pixelRatio;
this.stretchX = stretchX;
this.stretchY = stretchY;
this.content = content;
this.version = version;
}

Expand Down
3 changes: 3 additions & 0 deletions src/render/image_manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@ class ImageManager extends Evented {
pixelRatio: image.pixelRatio,
sdf: image.sdf,
version: image.version,
stretchX: image.stretchX,
stretchY: image.stretchY,
content: image.content,
hasRenderCallback: Boolean(image.userImage && image.userImage.render)
};
} else {
Expand Down
6 changes: 5 additions & 1 deletion src/shaders/symbol_icon.vertex.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const float PI = 3.141592653589793;

attribute vec4 a_pos_offset;
attribute vec4 a_data;
attribute vec4 a_pixeloffset;
attribute vec3 a_projected_pos;
attribute float a_fade_opacity;

Expand Down Expand Up @@ -39,6 +40,9 @@ void main() {
vec2 a_size = a_data.zw;

float a_size_min = floor(a_size[0] * 0.5);
vec2 a_pxoffset = a_pixeloffset.xy;
vec2 a_minFontScale = a_pixeloffset.zw / 256.0;

highp float segment_angle = -a_projected_pos[2];
float size;

Expand Down Expand Up @@ -81,7 +85,7 @@ void main() {
mat2 rotation_matrix = mat2(angle_cos, -1.0 * angle_sin, angle_sin, angle_cos);

vec4 projected_pos = u_label_plane_matrix * vec4(a_projected_pos.xy, 0.0, 1.0);
gl_Position = u_coord_matrix * vec4(projected_pos.xy / projected_pos.w + rotation_matrix * (a_offset / 32.0 * fontScale), 0.0, 1.0);
gl_Position = u_coord_matrix * vec4(projected_pos.xy / projected_pos.w + rotation_matrix * (a_offset / 32.0 * max(a_minFontScale, fontScale) + a_pxoffset / 16.0), 0.0, 1.0);

v_tex = a_tex / u_texsize;
vec2 fade_opacity = unpack_opacity(a_fade_opacity);
Expand Down
5 changes: 4 additions & 1 deletion src/shaders/symbol_sdf.vertex.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const float PI = 3.141592653589793;

attribute vec4 a_pos_offset;
attribute vec4 a_data;
attribute vec4 a_pixeloffset;
attribute vec3 a_projected_pos;
attribute float a_fade_opacity;

Expand Down Expand Up @@ -51,6 +52,8 @@ void main() {
vec2 a_size = a_data.zw;

float a_size_min = floor(a_size[0] * 0.5);
vec2 a_pxoffset = a_pixeloffset.xy;

highp float segment_angle = -a_projected_pos[2];
float size;

Expand Down Expand Up @@ -100,7 +103,7 @@ void main() {
mat2 rotation_matrix = mat2(angle_cos, -1.0 * angle_sin, angle_sin, angle_cos);

vec4 projected_pos = u_label_plane_matrix * vec4(a_projected_pos.xy, 0.0, 1.0);
gl_Position = u_coord_matrix * vec4(projected_pos.xy / projected_pos.w + rotation_matrix * (a_offset / 32.0 * fontScale), 0.0, 1.0);
gl_Position = u_coord_matrix * vec4(projected_pos.xy / projected_pos.w + rotation_matrix * (a_offset / 32.0 * fontScale + a_pxoffset), 0.0, 1.0);
float gamma_scale = gl_Position.w;

vec2 fade_opacity = unpack_opacity(a_fade_opacity);
Expand Down
4 changes: 2 additions & 2 deletions src/style/load_sprite.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ export default function(baseURL: string,
const result = {};

for (const id in json) {
const {width, height, x, y, sdf, pixelRatio} = json[id];
const {width, height, x, y, sdf, pixelRatio, stretchX, stretchY, content} = json[id];
const data = new RGBAImage({width, height});
RGBAImage.copy(imageData, data, {x, y}, {x: 0, y: 0}, {width, height});
result[id] = {data, pixelRatio, sdf};
result[id] = {data, pixelRatio, sdf, stretchX, stretchY, content};
}

callback(null, result);
Expand Down
3 changes: 3 additions & 0 deletions src/style/style_image.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ export type StyleImage = {
data: RGBAImage,
pixelRatio: number,
sdf: boolean,
stretchX: any,
stretchY: any,
content: any,
version: number,
hasRenderCallback?: boolean,
userImage?: StyleImageInterface
Expand Down
Loading

0 comments on commit 9f01202

Please sign in to comment.