Merge branch 'master' into label-styles-line-break
Conflicts: bin/style.js
This commit is contained in:
2
Makefile
2
Makefile
@@ -48,7 +48,7 @@ linux: config.gypi linux/llmr-app.gyp node
|
||||
|
||||
# Executes the Linux binary
|
||||
run-linux: linux
|
||||
build/$(BUILDTYPE)/llmr
|
||||
build/$(BUILDTYPE)/mapbox-gl
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[](https://magnum.travis-ci.com/mapbox/llmr-native)
|
||||
[](https://magnum.travis-ci.com/mapbox/mapbox-gl-native)
|
||||
|
||||
An OpenGL renderer for [Mapbox vector tiles](https://www.mapbox.com/blog/vector-tiles),
|
||||
implemented in C++11, currently targeting iOS, OS X, and Ubuntu Linux.
|
||||
|
||||
16
bin/style.js
16
bin/style.js
@@ -765,6 +765,7 @@ module.exports = {
|
||||
"path": "horizontal",
|
||||
"font": "Open Sans Semibold, Arial Unicode MS Bold",
|
||||
"fontSize": 18,
|
||||
"maxWidth": 6,
|
||||
"feature_type": "point",
|
||||
"type": "text"
|
||||
},
|
||||
@@ -878,6 +879,9 @@ module.exports = {
|
||||
"text_field": "name",
|
||||
"path": "horizontal",
|
||||
"padding": 2,
|
||||
"maxWidth": 8,
|
||||
"verticalAlignment": "top",
|
||||
"translate": [0, -1],
|
||||
"font": "Open Sans Semibold, Arial Unicode MS Bold",
|
||||
"fontSize": 12,
|
||||
"feature_type": "point",
|
||||
@@ -892,6 +896,9 @@ module.exports = {
|
||||
"text_field": "name",
|
||||
"path": "horizontal",
|
||||
"padding": 2,
|
||||
"maxWidth": 8,
|
||||
"verticalAlignment": "top",
|
||||
"translate": [0, -1],
|
||||
"font": "Open Sans Semibold, Arial Unicode MS Bold",
|
||||
"fontSize": 11,
|
||||
"feature_type": "point",
|
||||
@@ -906,6 +913,9 @@ module.exports = {
|
||||
"text_field": "name",
|
||||
"path": "horizontal",
|
||||
"padding": 2,
|
||||
"maxWidth": 8,
|
||||
"verticalAlignment": "top",
|
||||
"translate": [0, -1],
|
||||
"font": "Open Sans Semibold, Arial Unicode MS Bold",
|
||||
"fontSize": 10,
|
||||
"feature_type": "point",
|
||||
@@ -2429,7 +2439,6 @@ module.exports = {
|
||||
"stroke": "land",
|
||||
"strokeWidth": 0.3,
|
||||
"strokeBlur": 1,
|
||||
//"translate": [0, 14]
|
||||
},
|
||||
"poi_label_3": {
|
||||
"color": "#444",
|
||||
@@ -2437,7 +2446,6 @@ module.exports = {
|
||||
"stroke": "land",
|
||||
"strokeWidth": 0.3,
|
||||
"strokeBlur": 1,
|
||||
//"translate": [0, 14],
|
||||
"opacity": [
|
||||
"stops",
|
||||
{"z": 16.5, "val": 0},
|
||||
@@ -2455,7 +2463,6 @@ module.exports = {
|
||||
"stroke": "land",
|
||||
"strokeWidth": 0.3,
|
||||
"strokeBlur": 1,
|
||||
//"translate": [0, 14]
|
||||
},
|
||||
"poi_aerodrome": {
|
||||
"opacity": ["stops",
|
||||
@@ -3181,7 +3188,6 @@ module.exports = {
|
||||
"stroke": "text2_stroke_night",
|
||||
"strokeWidth": 0.3,
|
||||
"strokeBlur": 1,
|
||||
//"translate": [0, 14]
|
||||
},
|
||||
"poi_label_3": {
|
||||
"color": "#fff",
|
||||
@@ -3189,7 +3195,6 @@ module.exports = {
|
||||
"stroke": "text2_stroke_night",
|
||||
"strokeWidth": 0.3,
|
||||
"strokeBlur": 1,
|
||||
//"translate": [0, 14],
|
||||
"opacity": [
|
||||
"stops",
|
||||
{"z": 16.5, "val": 0},
|
||||
@@ -3207,7 +3212,6 @@ module.exports = {
|
||||
"stroke": "text2_stroke_night",
|
||||
"strokeWidth": 0.3,
|
||||
"strokeBlur": 1,
|
||||
//"translate": [0, 14]
|
||||
},
|
||||
"poi_aerodrome": {
|
||||
"opacity": ["stops",
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <cmath>
|
||||
|
||||
#include "value.hpp"
|
||||
#include "../util/vec.hpp"
|
||||
|
||||
namespace llmr {
|
||||
|
||||
@@ -68,6 +69,18 @@ inline TextPathType textPathType(const std::string& path) {
|
||||
else return TextPathType::Horizontal;
|
||||
};
|
||||
|
||||
inline float alignmentType(const std::string& alignment) {
|
||||
if (alignment == "right") return 1.0f;
|
||||
else if (alignment == "left") return 0.0f;
|
||||
else return 0.5;
|
||||
};
|
||||
|
||||
inline float verticalAlignmentType(const std::string& alignment) {
|
||||
if (alignment == "bottom") return 1.0f;
|
||||
else if (alignment == "top") return 0.0f;
|
||||
else return 0.5;
|
||||
};
|
||||
|
||||
class BucketGeometryDescription {
|
||||
public:
|
||||
CapType cap = CapType::None;
|
||||
@@ -79,6 +92,12 @@ public:
|
||||
float miter_limit = 2.0f;
|
||||
float round_limit = 1.0f;
|
||||
TextPathType path = TextPathType::Horizontal;
|
||||
float alignment = 0.5;
|
||||
float vertical_alignment = 0.5;
|
||||
float line_height = 1.2 * 24;
|
||||
float max_width = 15.0f * 24;
|
||||
float letter_spacing = 0;
|
||||
vec2<float> translate {0, 0};
|
||||
float padding = 2.0f;
|
||||
float textMinDistance = 250.0f;
|
||||
float rotate = 0.0f; // what is this?
|
||||
|
||||
@@ -51,8 +51,8 @@ public:
|
||||
|
||||
uint32_t face = 0;
|
||||
uint32_t glyph = 0;
|
||||
uint32_t x = 0;
|
||||
uint32_t y = 0;
|
||||
int32_t x = 0;
|
||||
int32_t y = 0;
|
||||
};
|
||||
|
||||
typedef std::vector<GlyphPlacement> Shaping;
|
||||
|
||||
@@ -30,7 +30,17 @@ public:
|
||||
void insert(uint32_t id, const SDFGlyph &glyph);
|
||||
const std::map<uint32_t, GlyphMetrics> &getMetrics() const;
|
||||
const std::map<uint32_t, SDFGlyph> &getSDFs() const;
|
||||
const Shaping getShaping(const std::u32string &string) const;
|
||||
const Shaping getShaping(const std::u32string &string,
|
||||
const float &maxWidth,
|
||||
const float &lineHeight,
|
||||
const float &alignment,
|
||||
const float &verticalAlignment,
|
||||
const float &letterSpacing) const;
|
||||
Shaping lineWrap(Shaping &shaping,
|
||||
const float &lineHeight,
|
||||
const float &maxWidth,
|
||||
const float &alignment,
|
||||
const float &verticalAlignment) const;
|
||||
|
||||
private:
|
||||
std::map<uint32_t, std::string> bitmaps;
|
||||
|
||||
@@ -20,8 +20,6 @@ public:
|
||||
const BucketGeometryDescription &info,
|
||||
const GlyphPositions &face,
|
||||
const Shaping &shaping);
|
||||
float measureText(const GlyphPositions &face,
|
||||
const Shaping &shaping);
|
||||
|
||||
private:
|
||||
const int8_t zoom;
|
||||
|
||||
@@ -122,7 +122,7 @@ private:
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<loop> loop;
|
||||
std::shared_ptr<uv::loop> loop;
|
||||
uv_work_t req;
|
||||
T data;
|
||||
work_callback work_cb;
|
||||
|
||||
@@ -19,7 +19,9 @@ ensure_dep pkg-config
|
||||
ensure_dep node
|
||||
if [ ${UNAME} = 'Darwin' ]; then
|
||||
ensure_dep makedepend
|
||||
ensure_dep glibtool
|
||||
if [[ ! `which libtool` ]] && [[ ! `which glibtool` ]]; then
|
||||
MISSING_DEPS="$MISSING_DEPS libtool"
|
||||
fi
|
||||
elif [ ${UNAME} = 'Linux' ]; then
|
||||
ensure_dep libtool
|
||||
ensure_dep gccmakedep
|
||||
|
||||
@@ -242,7 +242,9 @@ std::unique_ptr<Bucket> TileParser::createTextBucket(const VectorTileLayer &laye
|
||||
const std::u32string string = ucs4conv.convert(source_string);
|
||||
|
||||
// Shape labels.
|
||||
const Shaping shaping = fontStack.getShaping(string);
|
||||
const Shaping shaping = fontStack.getShaping(string, bucket_desc.geometry.max_width,
|
||||
bucket_desc.geometry.line_height, bucket_desc.geometry.alignment,
|
||||
bucket_desc.geometry.vertical_alignment, bucket_desc.geometry.letter_spacing);
|
||||
|
||||
// Place labels.
|
||||
addGlyph(tile.id.to_uint64(), bucket_desc.geometry.font, string, fontStack, *glyphAtlas,
|
||||
|
||||
@@ -102,6 +102,43 @@ BucketDescription StyleParser::parseBucket(JSVal value) {
|
||||
} else {
|
||||
throw Style::exception("curve must be a string");
|
||||
}
|
||||
} else if (name == "alignment") {
|
||||
if (value.IsString()) {
|
||||
bucket.geometry.alignment = alignmentType({ value.GetString(), value.GetStringLength() });
|
||||
} else {
|
||||
throw Style::exception("alignment must be a string");
|
||||
}
|
||||
} else if (name == "translate") {
|
||||
if (value.IsArray()) {
|
||||
bucket.geometry.translate.x = value[(rapidjson::SizeType)0].GetDouble() * 24;
|
||||
bucket.geometry.translate.y = value[(rapidjson::SizeType)1].GetDouble() * -24;
|
||||
} else {
|
||||
throw Style::exception("translate must be a string");
|
||||
}
|
||||
} else if (name == "verticalAlignment") {
|
||||
if (value.IsString()) {
|
||||
bucket.geometry.vertical_alignment = verticalAlignmentType({ value.GetString(), value.GetStringLength() });
|
||||
} else {
|
||||
throw Style::exception("verticalAlignment must be a string");
|
||||
}
|
||||
} else if (name == "lineHeight") {
|
||||
if (value.IsNumber()) {
|
||||
bucket.geometry.line_height = value.GetDouble() * 24;
|
||||
} else {
|
||||
throw Style::exception("line height must be a number");
|
||||
}
|
||||
} else if (name == "maxWidth") {
|
||||
if (value.IsNumber()) {
|
||||
bucket.geometry.max_width = value.GetDouble() * 24;
|
||||
} else {
|
||||
throw Style::exception("max width must be a number");
|
||||
}
|
||||
} else if (name == "letterSpacing") {
|
||||
if (value.IsNumber()) {
|
||||
bucket.geometry.letter_spacing = value.GetDouble() * 24;
|
||||
} else {
|
||||
throw Style::exception("letter spacing must be a number");
|
||||
}
|
||||
} else if (name == "miterLimit") {
|
||||
if (value.IsNumber()) {
|
||||
bucket.geometry.miter_limit = value.GetDouble();
|
||||
|
||||
@@ -28,23 +28,84 @@ const std::map<uint32_t, SDFGlyph> &FontStack::getSDFs() const {
|
||||
return sdfs;
|
||||
}
|
||||
|
||||
const Shaping FontStack::getShaping(const std::u32string &string) const {
|
||||
const Shaping FontStack::getShaping(const std::u32string &string, const float &maxWidth,
|
||||
const float &lineHeight, const float &alignment, const float &verticalAlignment,
|
||||
const float &letterSpacing) const {
|
||||
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
uint32_t i = 0;
|
||||
uint32_t x = 0;
|
||||
Shaping shaped;
|
||||
Shaping shaping;
|
||||
// Loop through all characters of this label and shape.
|
||||
for (uint32_t chr : string) {
|
||||
GlyphPlacement glyph = GlyphPlacement(0, chr, x, 0);
|
||||
shaped.push_back(glyph);
|
||||
shaping.push_back(glyph);
|
||||
i++;
|
||||
x += metrics.find(chr)->second.advance + letterSpacing;
|
||||
}
|
||||
|
||||
auto metrics_it = metrics.find(chr);
|
||||
if (metrics_it != metrics.end()) {
|
||||
x += metrics_it->second.advance;
|
||||
lineWrap(shaping, lineHeight, maxWidth, alignment, verticalAlignment);
|
||||
|
||||
return shaping;
|
||||
}
|
||||
|
||||
void alignVertically(Shaping &shaping, const uint32_t &lines, const float &lineHeight, const float &verticalAlign) {
|
||||
const float dy = -(lineHeight * (lines - 1) + 24) * verticalAlign - 5;
|
||||
for (GlyphPlacement &shape : shaping) {
|
||||
shape.y += dy;
|
||||
}
|
||||
}
|
||||
|
||||
void alignHorizontally(Shaping &shaping, const std::map<uint32_t, GlyphMetrics> &metrics,
|
||||
const uint32_t &start, const uint32_t &end, const float &alignment) {
|
||||
|
||||
uint32_t lastAdvance = metrics.find(shaping[end].glyph)->second.advance;
|
||||
int32_t lineIndent = (shaping[end].x + lastAdvance) * alignment;
|
||||
|
||||
for (uint32_t j = start; j <= end; j++) {
|
||||
shaping[j].x -= lineIndent;
|
||||
}
|
||||
}
|
||||
|
||||
Shaping FontStack::lineWrap(Shaping &shaping, const float &lineHeight, const float &maxWidth,
|
||||
const float &alignment, const float &verticalAlignment) const {
|
||||
|
||||
uint32_t lastSafeBreak = 0;
|
||||
uint32_t lengthBeforeCurrentLine = 0;
|
||||
uint32_t lineStartIndex = 0;
|
||||
uint32_t line = 0;
|
||||
|
||||
for (uint32_t i = 0; i < shaping.size(); i++) {
|
||||
GlyphPlacement &shape = shaping[i];
|
||||
|
||||
shape.x -= lengthBeforeCurrentLine;
|
||||
shape.y += lineHeight * line;
|
||||
|
||||
if (shape.x > maxWidth && lastSafeBreak != 0) {
|
||||
|
||||
uint32_t lineLength = shaping[lastSafeBreak + 1].x;
|
||||
|
||||
for (uint32_t k = lastSafeBreak + 1; k <= i; k++) {
|
||||
shaping[k].y += lineHeight;
|
||||
shaping[k].x -= lineLength;
|
||||
}
|
||||
|
||||
if (alignment) {
|
||||
alignHorizontally(shaping, metrics, lineStartIndex, lastSafeBreak - 1, alignment);
|
||||
}
|
||||
|
||||
lineStartIndex = lastSafeBreak + 1;
|
||||
lastSafeBreak = 0;
|
||||
lengthBeforeCurrentLine += lineLength;
|
||||
line++;
|
||||
}
|
||||
if (shape.glyph == 32) {
|
||||
lastSafeBreak = i;
|
||||
}
|
||||
}
|
||||
return shaped;
|
||||
|
||||
alignHorizontally(shaping, metrics, lineStartIndex, shaping.size() - 1, alignment);
|
||||
alignVertically(shaping, line + 1, lineHeight, verticalAlignment);
|
||||
}
|
||||
|
||||
GlyphPBF::GlyphPBF(const std::string &fontStack, GlyphRange glyphRange)
|
||||
|
||||
@@ -137,15 +137,12 @@ void getSegmentGlyphs(std::back_insert_iterator<GlyphInstances> glyphs,
|
||||
}
|
||||
|
||||
void getGlyphs(PlacedGlyphs &glyphs, GlyphBoxes &boxes,
|
||||
Anchor &anchor, float advance, const Shaping &shaping,
|
||||
Anchor &anchor, vec2<float> origin, const Shaping &shaping,
|
||||
const GlyphPositions &face, float fontScale,
|
||||
bool horizontal, const std::vector<Coordinate> &line,
|
||||
float maxAngleDelta, float rotate) {
|
||||
// The total text advance is the width of this label.
|
||||
|
||||
// TODO: figure out correct ascender height.
|
||||
vec2<float> origin{-advance / 2, -17};
|
||||
|
||||
// TODO: allow setting an alignment
|
||||
// var alignment = 'center';
|
||||
// if (alignment == 'center') {
|
||||
@@ -261,11 +258,8 @@ void Placement::addFeature(TextBucket& bucket,
|
||||
const float fontScale =
|
||||
(tileExtent / util::tileSize) / (glyphSize / info.size);
|
||||
|
||||
const float advance = measureText(face, shaping);
|
||||
Anchors anchors;
|
||||
|
||||
// fprintf(stderr, "adding feature with advance %f\n", advance);
|
||||
|
||||
if (line.size() == 1) {
|
||||
// Point labels
|
||||
anchors = Anchors{{Anchor{
|
||||
@@ -285,7 +279,7 @@ void Placement::addFeature(TextBucket& bucket,
|
||||
PlacedGlyphs glyphs;
|
||||
GlyphBoxes boxes;
|
||||
|
||||
getGlyphs(glyphs, boxes, anchor, advance, shaping, face, fontScale, horizontal,
|
||||
getGlyphs(glyphs, boxes, anchor, info.translate, shaping, face, fontScale, horizontal,
|
||||
line, maxAngleDelta, rotate);
|
||||
PlacementProperty place =
|
||||
collision.place(boxes, anchor, anchor.scale, maxPlacementScale,
|
||||
@@ -295,16 +289,3 @@ void Placement::addFeature(TextBucket& bucket,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float Placement::measureText(const GlyphPositions &face,
|
||||
const Shaping &shaping) {
|
||||
float advance = 0;
|
||||
|
||||
// TODO: advance is not calculated correctly. we should instead use the
|
||||
// bounding box of the glyph placement.
|
||||
for (const GlyphPlacement &shape : shaping) {
|
||||
advance += getGlyph(shape, face).metrics.advance;
|
||||
}
|
||||
|
||||
return advance;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user