Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Commit

Permalink
[core] improve legibility of labels that follow lines
Browse files Browse the repository at this point in the history
port mapbox/mapbox-gl-js#4781

This improves legibility of labels that follow lines in pitched views.
The previous approach used the limited information in the shader to
calculate put the glyph in approximatelyright place. The new approach
does this more accurately by doing it on the cpu where we have access to
the entire line geometry.
  • Loading branch information
ansis committed Jul 10, 2017
1 parent 5cfa15e commit 9034544
Show file tree
Hide file tree
Showing 32 changed files with 729 additions and 743 deletions.
2 changes: 2 additions & 0 deletions cmake/core-files.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ set(MBGL_CORE_FILES
src/mbgl/layout/symbol_instance.hpp
src/mbgl/layout/symbol_layout.cpp
src/mbgl/layout/symbol_layout.hpp
src/mbgl/layout/symbol_projection.cpp
src/mbgl/layout/symbol_projection.hpp

# map
include/mbgl/map/backend.hpp
Expand Down
9 changes: 7 additions & 2 deletions src/mbgl/gl/context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,15 +164,20 @@ void Context::verifyProgramLinkage(ProgramID program_) {
throw std::runtime_error("program failed to link");
}

UniqueBuffer Context::createVertexBuffer(const void* data, std::size_t size) {
UniqueBuffer Context::createVertexBuffer(const void* data, std::size_t size, const BufferUsageType usage) {
BufferID id = 0;
MBGL_CHECK_ERROR(glGenBuffers(1, &id));
UniqueBuffer result { std::move(id), { this } };
vertexBuffer = result;
MBGL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW));
MBGL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, size, data, static_cast<GLenum>(usage)));
return result;
}

void Context::updateVertexBuffer(UniqueBuffer& buffer, const void* data, std::size_t size) {
vertexBuffer = buffer;
MBGL_CHECK_ERROR(glBufferSubData(GL_ARRAY_BUFFER, 0, size, data));
}

UniqueBuffer Context::createIndexBuffer(const void* data, std::size_t size) {
BufferID id = 0;
MBGL_CHECK_ERROR(glGenBuffers(1, &id));
Expand Down
13 changes: 10 additions & 3 deletions src/mbgl/gl/context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,19 @@ class Context : private util::noncopyable {
optional<std::pair<BinaryProgramFormat, std::string>> getBinaryProgram(ProgramID) const;

template <class Vertex, class DrawMode>
VertexBuffer<Vertex, DrawMode> createVertexBuffer(VertexVector<Vertex, DrawMode>&& v) {
VertexBuffer<Vertex, DrawMode> createVertexBuffer(VertexVector<Vertex, DrawMode>&& v, const BufferUsageType usage=BufferUsageType::StaticDraw) {
return VertexBuffer<Vertex, DrawMode> {
v.vertexSize(),
createVertexBuffer(v.data(), v.byteSize())
createVertexBuffer(v.data(), v.byteSize(), usage)
};
}

template <class Vertex, class DrawMode>
void updateVertexBuffer(VertexBuffer<Vertex, DrawMode>& buffer, VertexVector<Vertex, DrawMode>&& v) {
assert(v.vertexSize() == buffer.vertexCount);
updateVertexBuffer(buffer.buffer, v.data(), v.byteSize());
}

template <class DrawMode>
IndexBuffer<DrawMode> createIndexBuffer(IndexVector<DrawMode>&& v) {
return IndexBuffer<DrawMode> {
Expand Down Expand Up @@ -239,7 +245,8 @@ class Context : private util::noncopyable {
State<value::PointSize> pointSize;
#endif // MBGL_USE_GLES2

UniqueBuffer createVertexBuffer(const void* data, std::size_t size);
UniqueBuffer createVertexBuffer(const void* data, std::size_t size, const BufferUsageType usage);
void updateVertexBuffer(UniqueBuffer& buffer, const void* data, std::size_t size);
UniqueBuffer createIndexBuffer(const void* data, std::size_t size);
UniqueTexture createTexture(Size size, const void* data, TextureFormat, TextureUnit);
void updateTexture(TextureID, Size size, const void* data, TextureFormat, TextureUnit);
Expand Down
6 changes: 6 additions & 0 deletions src/mbgl/gl/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,5 +96,11 @@ enum class UniformDataType : uint32_t {
SamplerCube = 0x8B60,
};

enum class BufferUsageType : uint32_t {
StreamDraw = 0x88E0,
StaticDraw = 0x88E4,
DynamicDraw = 0x88E8,
};

} // namespace gl
} // namespace mbgl
23 changes: 14 additions & 9 deletions src/mbgl/layout/symbol_instance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ namespace mbgl {

using namespace style;

SymbolInstance::SymbolInstance(Anchor& anchor,
const GeometryCoordinates& line,
SymbolInstance::SymbolInstance(Anchor& anchor_,
GeometryCoordinates line_,
const std::pair<Shaping, Shaping>& shapedTextOrientations,
optional<PositionedIcon> shapedIcon,
const SymbolLayoutProperties::Evaluated& layout,
Expand All @@ -16,33 +16,38 @@ SymbolInstance::SymbolInstance(Anchor& anchor,
const float textBoxScale,
const float textPadding,
const SymbolPlacementType textPlacement,
const std::array<float, 2> textOffset_,
const float iconBoxScale,
const float iconPadding,
const SymbolPlacementType iconPlacement,
const std::array<float, 2> iconOffset_,
const GlyphPositionMap& positions,
const IndexedSubfeature& indexedFeature,
const std::size_t featureIndex_) :
point(anchor.point),
anchor(anchor_),
line(line_),
index(index_),
hasText(shapedTextOrientations.first || shapedTextOrientations.second),
hasIcon(shapedIcon),

// Create the collision features that will be used to check whether this symbol instance can be placed
textCollisionFeature(line, anchor, shapedTextOrientations.second ?: shapedTextOrientations.first, textBoxScale, textPadding, textPlacement, indexedFeature),
iconCollisionFeature(line, anchor, shapedIcon, iconBoxScale, iconPadding, iconPlacement, indexedFeature),
featureIndex(featureIndex_) {
textCollisionFeature(line_, anchor, shapedTextOrientations.second ?: shapedTextOrientations.first, textBoxScale, textPadding, textPlacement, indexedFeature),
iconCollisionFeature(line_, anchor, shapedIcon, iconBoxScale, iconPadding, iconPlacement, indexedFeature),
featureIndex(featureIndex_),
textOffset(textOffset_),
iconOffset(iconOffset_) {

// Create the quads used for rendering the icon and glyphs.
if (addToBuffers) {
if (shapedIcon) {
iconQuad = getIconQuad(anchor, *shapedIcon, line, layout, layoutTextSize, iconPlacement, shapedTextOrientations.first);
iconQuad = getIconQuad(*shapedIcon, layout, layoutTextSize, shapedTextOrientations.first);
}
if (shapedTextOrientations.first) {
auto quads = getGlyphQuads(anchor, shapedTextOrientations.first, textBoxScale, line, layout, textPlacement, positions);
auto quads = getGlyphQuads(shapedTextOrientations.first, layout, textPlacement, positions);
glyphQuads.insert(glyphQuads.end(), quads.begin(), quads.end());
}
if (shapedTextOrientations.second) {
auto quads = getGlyphQuads(anchor, shapedTextOrientations.second, textBoxScale, line, layout, textPlacement, positions);
auto quads = getGlyphQuads(shapedTextOrientations.second, layout, textPlacement, positions);
glyphQuads.insert(glyphQuads.end(), quads.begin(), quads.end());
}
}
Expand Down
9 changes: 7 additions & 2 deletions src/mbgl/layout/symbol_instance.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class IndexedSubfeature;
class SymbolInstance {
public:
SymbolInstance(Anchor& anchor,
const GeometryCoordinates& line,
GeometryCoordinates line,
const std::pair<Shaping, Shaping>& shapedTextOrientations,
optional<PositionedIcon> shapedIcon,
const style::SymbolLayoutProperties::Evaluated&,
Expand All @@ -23,14 +23,17 @@ class SymbolInstance {
const float textBoxScale,
const float textPadding,
style::SymbolPlacementType textPlacement,
const std::array<float, 2> textOffset,
const float iconBoxScale,
const float iconPadding,
style::SymbolPlacementType iconPlacement,
const std::array<float, 2> iconOffset,
const GlyphPositionMap&,
const IndexedSubfeature&,
const std::size_t featureIndex);

Point<float> point;
Anchor anchor;
GeometryCoordinates line;
uint32_t index;
bool hasText;
bool hasIcon;
Expand All @@ -40,6 +43,8 @@ class SymbolInstance {
CollisionFeature iconCollisionFeature;
WritingModeType writingModes;
std::size_t featureIndex;
std::array<float, 2> textOffset;
std::array<float, 2> iconOffset;
};

} // namespace mbgl
98 changes: 45 additions & 53 deletions src/mbgl/layout/symbol_layout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,8 @@ void SymbolLayout::addFeature(const std::size_t index,

const float layoutTextSize = layout.evaluate<TextSize>(zoom + 1, feature);
const float layoutIconSize = layout.evaluate<IconSize>(zoom + 1, feature);
const std::array<float, 2> textOffset = layout.evaluate<TextOffset>(zoom, feature);
const std::array<float, 2> iconOffset = layout.evaluate<IconOffset>(zoom, feature);

// 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.
Expand Down Expand Up @@ -355,8 +357,8 @@ void SymbolLayout::addFeature(const std::size_t index,
symbolInstances.emplace_back(anchor, line, shapedTextOrientations, shapedIcon,
layout.evaluate(zoom, feature), layoutTextSize,
addToBuffers, symbolInstances.size(),
textBoxScale, textPadding, textPlacement,
iconBoxScale, iconPadding, iconPlacement,
textBoxScale, textPadding, textPlacement, textOffset,
iconBoxScale, iconPadding, iconPlacement, iconOffset,
glyphPositionMap, indexedFeature, index);
};

Expand Down Expand Up @@ -455,8 +457,8 @@ std::unique_ptr<SymbolBucket> SymbolLayout::place(CollisionTile& collisionTile)
const float cos = std::cos(collisionTile.config.angle);

std::sort(symbolInstances.begin(), symbolInstances.end(), [sin, cos](SymbolInstance &a, SymbolInstance &b) {
const int32_t aRotated = sin * a.point.x + cos * a.point.y;
const int32_t bRotated = sin * b.point.x + cos * b.point.y;
const int32_t aRotated = sin * a.anchor.point.x + cos * a.anchor.point.y;
const int32_t bRotated = sin * b.anchor.point.x + cos * b.anchor.point.y;
return aRotated != bRotated ?
aRotated < bRotated :
a.index > b.index;
Expand Down Expand Up @@ -501,10 +503,21 @@ std::unique_ptr<SymbolBucket> SymbolLayout::place(CollisionTile& collisionTile)
const float placementZoom = util::max(util::log2(glyphScale) + zoom, 0.0f);
collisionTile.insertFeature(symbolInstance.textCollisionFeature, glyphScale, layout.get<TextIgnorePlacement>());
if (glyphScale < collisionTile.maxScale) {

const float labelAngle = std::fmod((symbolInstance.anchor.angle + collisionTile.config.angle) + 2 * M_PI, 2 * M_PI);
const bool inVerticalRange = (
(labelAngle > M_PI * 1.0 / 4.0 && labelAngle <= M_PI * 3.0 / 4) ||
(labelAngle > M_PI * 5.0 / 4.0 && labelAngle <= M_PI * 7.0 / 4));
const bool useVerticalMode = symbolInstance.writingModes & WritingModeType::Vertical && inVerticalRange;

const Range<float> sizeData = bucket->textSizeBinder->getVertexSizeData(feature);
bucket->text.placedSymbols.emplace_back(symbolInstance.anchor.point, symbolInstance.anchor.segment, sizeData.min, sizeData.max,
symbolInstance.textOffset, placementZoom, useVerticalMode, symbolInstance.line);

for (const auto& symbol : symbolInstance.glyphQuads) {
addSymbol(
bucket->text, *bucket->textSizeBinder, symbol, feature, placementZoom,
keepUpright, textPlacement, collisionTile.config.angle, symbolInstance.writingModes, symbolInstance.point);
bucket->text, sizeData, symbol, placementZoom,
keepUpright, textPlacement, symbolInstance.anchor, bucket->text.placedSymbols.back());
}
}
}
Expand All @@ -513,9 +526,12 @@ std::unique_ptr<SymbolBucket> SymbolLayout::place(CollisionTile& collisionTile)
const float placementZoom = util::max(util::log2(iconScale) + zoom, 0.0f);
collisionTile.insertFeature(symbolInstance.iconCollisionFeature, iconScale, layout.get<IconIgnorePlacement>());
if (iconScale < collisionTile.maxScale && symbolInstance.iconQuad) {
const Range<float> sizeData = bucket->iconSizeBinder->getVertexSizeData(feature);
bucket->icon.placedSymbols.emplace_back(symbolInstance.anchor.point, symbolInstance.anchor.segment, sizeData.min, sizeData.max,
symbolInstance.iconOffset, placementZoom, false, symbolInstance.line);
addSymbol(
bucket->icon, *bucket->iconSizeBinder, *symbolInstance.iconQuad, feature, placementZoom,
keepUpright, iconPlacement, collisionTile.config.angle, symbolInstance.writingModes, symbolInstance.point);
bucket->icon, sizeData, *symbolInstance.iconQuad, placementZoom,
keepUpright, iconPlacement, symbolInstance.anchor, bucket->icon.placedSymbols.back());
}
}

Expand All @@ -534,15 +550,13 @@ std::unique_ptr<SymbolBucket> SymbolLayout::place(CollisionTile& collisionTile)

template <typename Buffer>
void SymbolLayout::addSymbol(Buffer& buffer,
SymbolSizeBinder& sizeBinder,
const Range<float> sizeData,
const SymbolQuad& symbol,
const SymbolFeature& feature,
const float placementZoom,
const bool keepUpright,
const style::SymbolPlacementType placement,
const float placementAngle,
const WritingModeType writingModes,
const Point<float> labelAnchor) {
const Anchor& labelAnchor,
PlacedSymbol& placedSymbol) {
constexpr const uint16_t vertexLength = 4;

const auto &tl = symbol.tl;
Expand All @@ -551,30 +565,9 @@ void SymbolLayout::addSymbol(Buffer& buffer,
const auto &br = symbol.br;
const auto &tex = symbol.tex;

float minZoom = util::max(zoom + util::log2(symbol.minScale), placementZoom);
float maxZoom = util::min(zoom + util::log2(symbol.maxScale), util::MAX_ZOOM_F);
const auto &anchorPoint = symbol.anchorPoint;

// drop incorrectly oriented glyphs
const float a = std::fmod(symbol.anchorAngle + placementAngle + M_PI, M_PI * 2);
if (writingModes & WritingModeType::Vertical) {
if (placement == style::SymbolPlacementType::Line && symbol.writingMode == WritingModeType::Vertical) {
if (keepUpright && placement == style::SymbolPlacementType::Line && (a <= (M_PI * 5 / 4) || a > (M_PI * 7 / 4)))
return;
} else if (keepUpright && placement == style::SymbolPlacementType::Line && (a <= (M_PI * 3 / 4) || a > (M_PI * 5 / 4)))
return;
} else if (keepUpright && placement == style::SymbolPlacementType::Line &&
(a <= M_PI / 2 || a > M_PI * 3 / 2)) {
return;
}

if (maxZoom <= minZoom)
return;

// Lower min zoom so that while fading out the label
// it can be shown outside of collision-free zoom levels
if (minZoom == placementZoom) {
minZoom = 0;
if (placement == style::SymbolPlacementType::Line && keepUpright) {
// drop incorrectly oriented glyphs
if ((symbol.writingMode == WritingModeType::Vertical) != placedSymbol.useVerticalMode) return;
}

if (buffer.segments.empty() || buffer.segments.back().vertexLength + vertexLength > std::numeric_limits<uint16_t>::max()) {
Expand All @@ -587,27 +580,26 @@ void SymbolLayout::addSymbol(Buffer& buffer,
assert(segment.vertexLength <= std::numeric_limits<uint16_t>::max());
uint16_t index = segment.vertexLength;

// Encode angle of glyph
uint8_t glyphAngle = std::round((symbol.glyphAngle / (M_PI * 2)) * 256);

// coordinates (2 triangles)
buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(anchorPoint, tl, labelAnchor, tex.x, tex.y,
minZoom, maxZoom, placementZoom, glyphAngle));
buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(anchorPoint, tr, labelAnchor, tex.x + tex.w, tex.y,
minZoom, maxZoom, placementZoom, glyphAngle));
buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(anchorPoint, bl, labelAnchor, tex.x, tex.y + tex.h,
minZoom, maxZoom, placementZoom, glyphAngle));
buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(anchorPoint, br, labelAnchor, tex.x + tex.w, tex.y + tex.h,
minZoom, maxZoom, placementZoom, glyphAngle));
buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(labelAnchor.point, tl, symbol.glyphOffset.y, tex.x, tex.y, sizeData));
buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(labelAnchor.point, tr, symbol.glyphOffset.y, tex.x + tex.w, tex.y, sizeData));
buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(labelAnchor.point, bl, symbol.glyphOffset.y, tex.x, tex.y + tex.h, sizeData));
buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(labelAnchor.point, br, symbol.glyphOffset.y, tex.x + tex.w, tex.y + tex.h, sizeData));

sizeBinder.populateVertexVector(feature);
auto dynamicVertex = SymbolDynamicLayoutAttributes::vertex(labelAnchor.point, 0, placementZoom);
buffer.dynamicVertices.emplace_back(dynamicVertex);
buffer.dynamicVertices.emplace_back(dynamicVertex);
buffer.dynamicVertices.emplace_back(dynamicVertex);
buffer.dynamicVertices.emplace_back(dynamicVertex);

// add the two triangles, referencing the four coordinates we just inserted.
buffer.triangles.emplace_back(index + 0, index + 1, index + 2);
buffer.triangles.emplace_back(index + 1, index + 2, index + 3);

segment.vertexLength += vertexLength;
segment.indexLength += 6;

placedSymbol.glyphOffsets.push_back(symbol.glyphOffset.x);
}

void SymbolLayout::addToDebugBuffers(CollisionTile& collisionTile, SymbolBucket& bucket) {
Expand Down Expand Up @@ -647,10 +639,10 @@ void SymbolLayout::addToDebugBuffers(CollisionTile& collisionTile, SymbolBucket&
auto& segment = collisionBox.segments.back();
uint16_t index = segment.vertexLength;

collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.point, tl, maxZoom, placementZoom));
collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.point, tr, maxZoom, placementZoom));
collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.point, br, maxZoom, placementZoom));
collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.point, bl, maxZoom, placementZoom));
collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, tl, maxZoom, placementZoom));
collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, tr, maxZoom, placementZoom));
collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, br, maxZoom, placementZoom));
collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, bl, maxZoom, placementZoom));

collisionBox.lines.emplace_back(index + 0, index + 1);
collisionBox.lines.emplace_back(index + 1, index + 2);
Expand Down
Loading

0 comments on commit 9034544

Please sign in to comment.