port text alignment algorithm

This commit is contained in:
Konstantin Käfer
2014-08-05 16:18:23 +02:00
parent 05b95ab057
commit 898c5dc7c3
3 changed files with 88 additions and 64 deletions

View File

@@ -3,6 +3,7 @@
#include <mbgl/text/glyph.hpp>
#include <mbgl/util/pbf.hpp>
#include <mbgl/util/vec.hpp>
#include <cstdint>
#include <vector>
@@ -30,17 +31,11 @@ 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 float &maxWidth,
const float &lineHeight,
const float &alignment,
const float &verticalAlignment,
const float &letterSpacing) const;
void lineWrap(Shaping &shaping,
const float &lineHeight,
const float &maxWidth,
const float &alignment,
const float &verticalAlignment) const;
const Shaping getShaping(const std::u32string &string, float maxWidth, float lineHeight,
float horizontalAlign, float verticalAlign, float justify,
float spacing, const vec2<float> &translate) const;
void lineWrap(Shaping &shaping, float lineHeight, float maxWidth, float horizontalAlign,
float verticalAlign, float justify) const;
private:
std::map<uint32_t, std::string> bitmaps;

View File

@@ -126,6 +126,10 @@ void SymbolBucket::addFeatures(const VectorTileLayer &layer, const FilterExpress
else if (properties.text.vertical_align == TextVerticalAlignType::Top)
verticalAlign = 0;
float justify = 0.5;
if (properties.text.justify == TextJustifyType::Right) justify = 1;
else if (properties.text.justify == TextJustifyType::Left) justify = 0;
const FontStack &fontStack = glyphStore.getFontStack(properties.text.font);
for (const SymbolFeature &feature : features) {
@@ -135,9 +139,15 @@ void SymbolBucket::addFeatures(const VectorTileLayer &layer, const FilterExpress
// if feature has text, shape the text
if (feature.label.length()) {
shaping = fontStack.getShaping(feature.label, properties.text.max_width,
properties.text.line_height, horizontalAlign,
verticalAlign, properties.text.letter_spacing);
shaping = fontStack.getShaping(
/* string */ feature.label,
/* maxWidth */ properties.text.max_width,
/* lineHeight */ properties.text.line_height,
/* horizontalAlign */ horizontalAlign,
/* verticalAlign */ verticalAlign,
/* justify */ justify,
/* spacing */ properties.text.letter_spacing,
/* translate */ properties.text.offset);
// Add the glyphs we need for this label to the glyph atlas.
if (shaping.size()) {
@@ -219,7 +229,8 @@ void SymbolBucket::addFeature(const std::vector<Coordinate> &line, const Shaping
anchors = {Anchor{float(line[0].x), float(line[0].y), 0, minScale}};
}
const vec2<float> origin = {0, 0};
// TODO: figure out correct ascender height.
const vec2<float> origin = {0, -17};
for (Anchor &anchor : anchors) {

View File

@@ -6,6 +6,7 @@
#include <mbgl/util/pbf.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/token.hpp>
#include <mbgl/util/math.hpp>
#include <mbgl/platform/platform.hpp>
#include <uv.h>
#include <algorithm>
@@ -30,91 +31,108 @@ const std::map<uint32_t, SDFGlyph> &FontStack::getSDFs() const {
return sdfs;
}
const Shaping FontStack::getShaping(const std::u32string &string, const float &maxWidth,
const float &lineHeight, const float &alignment, const float &verticalAlignment,
const float &letterSpacing) const {
const Shaping FontStack::getShaping(const std::u32string &string, const float maxWidth,
const float lineHeight, const float horizontalAlign,
const float verticalAlign, const float justify,
const float spacing, const vec2<float> &translate) const {
std::lock_guard<std::mutex> lock(mtx);
uint32_t i = 0;
uint32_t x = 0;
Shaping shaping;
uint32_t x = translate.x;
const uint32_t y = translate.y;
// Loop through all characters of this label and shape.
for (uint32_t chr : string) {
shaping.emplace_back(chr, x, 0);
i++;
shaping.emplace_back(chr, x, y);
auto metric = metrics.find(chr);
if (metric != metrics.end()) {
x += metric->second.advance + letterSpacing;
x += metric->second.advance + spacing;
}
}
lineWrap(shaping, lineHeight, maxWidth, alignment, verticalAlignment);
if (!shaping.size())
return shaping;
lineWrap(shaping, lineHeight, maxWidth, horizontalAlign, verticalAlign, justify);
return shaping;
}
void alignVertically(Shaping &shaping, const uint32_t &lines, const float &lineHeight, const float &verticalAlign) {
// TODO: figure out correct ascender height.
const float dy = -(lineHeight * (lines - 1) + 24) * verticalAlign - 5;
for (PositionedGlyph &shape : shaping) {
shape.y += dy;
void align(Shaping &shaping, const float justify, const float horizontalAlign,
const float verticalAlign, const uint32_t maxLineLength, const float lineHeight,
const uint32_t line) {
const float shiftX = (justify - horizontalAlign) * maxLineLength;
const float shiftY = (-verticalAlign * (line + 1) + 0.5) * lineHeight;
for (PositionedGlyph &glyph : shaping) {
glyph.x += shiftX;
glyph.y += shiftY;
}
}
void alignHorizontally(Shaping &shaping, const std::map<uint32_t, GlyphMetrics> &metrics,
const uint32_t &start, const uint32_t &end, const float &alignment) {
auto & shape = shaping.at(end);
auto metric = metrics.find(shape.glyph);
void justifyLine(Shaping &shaping, const std::map<uint32_t, GlyphMetrics> &metrics, uint32_t start,
uint32_t end, float justify) {
PositionedGlyph &glyph = shaping[end];
auto metric = metrics.find(glyph.glyph);
if (metric != metrics.end()) {
uint32_t lastAdvance = metric->second.advance;
int32_t lineIndent = (shape.x + lastAdvance) * alignment;
const uint32_t lastAdvance = metric->second.advance;
const float lineIndent = float(glyph.x + lastAdvance) * justify;
for (uint32_t j = start; j <= end; j++) {
shaping[j].x -= lineIndent;
}
}
}
void FontStack::lineWrap(Shaping &shaping, const float &lineHeight, const float &maxWidth,
const float &alignment, const float &verticalAlignment) const {
std::size_t num_shapes = shaping.size();
if (!num_shapes) {
return;
}
void FontStack::lineWrap(Shaping &shaping, const float lineHeight, const float maxWidth,
const float horizontalAlign, const float verticalAlign,
const float justify) 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++) {
PositionedGlyph &shape = shaping[i];
shape.x -= lengthBeforeCurrentLine;
shape.y += lineHeight * line;
uint32_t maxLineLength = 0;
if (shape.x > maxWidth && lastSafeBreak > 0) {
if (maxWidth) {
for (uint32_t i = 0; i < shaping.size(); i++) {
PositionedGlyph &shape = shaping[i];
uint32_t lineLength = shaping[lastSafeBreak + 1].x;
shape.x -= lengthBeforeCurrentLine;
shape.y += lineHeight * line;
for (uint32_t k = lastSafeBreak + 1; k <= i; k++) {
shaping[k].y += lineHeight;
shaping[k].x -= lineLength;
if (shape.x > maxWidth && lastSafeBreak > 0) {
uint32_t lineLength = shaping[lastSafeBreak + 1].x;
maxLineLength = util::max(lineLength, maxLineLength);
for (uint32_t k = lastSafeBreak + 1; k <= i; k++) {
shaping[k].y += lineHeight;
shaping[k].x -= lineLength;
}
if (justify) {
justifyLine(shaping, metrics, lineStartIndex, lastSafeBreak - 1, justify);
}
lineStartIndex = lastSafeBreak + 1;
lastSafeBreak = 0;
lengthBeforeCurrentLine += lineLength;
line++;
}
if (alignment) {
alignHorizontally(shaping, metrics, lineStartIndex, lastSafeBreak - 1, alignment);
if (shape.glyph == 32) {
lastSafeBreak = i;
}
lineStartIndex = lastSafeBreak + 1;
lastSafeBreak = 0;
lengthBeforeCurrentLine += lineLength;
line++;
}
if (shape.glyph == 32) {
lastSafeBreak = i;
}
}
alignHorizontally(shaping, metrics, lineStartIndex, shaping.size() - 1, alignment);
alignVertically(shaping, line + 1, lineHeight, verticalAlignment);
maxLineLength = maxLineLength || shaping.back().x;
justifyLine(shaping, metrics, lineStartIndex, shaping.size() - 1, justify);
align(shaping, justify, horizontalAlign, verticalAlign, maxLineLength, lineHeight, line);
}
GlyphPBF::GlyphPBF(const std::string &glyphURL, const std::string &fontStack, GlyphRange glyphRange)