From 898c5dc7c3ebf36c6a4df3ab62ad6aacfc82985a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20Ka=CC=88fer?= Date: Tue, 5 Aug 2014 16:18:23 +0200 Subject: port text alignment algorithm --- include/mbgl/text/glyph_store.hpp | 17 ++---- src/renderer/symbol_bucket.cpp | 19 +++++-- src/text/glyph_store.cpp | 116 ++++++++++++++++++++++---------------- 3 files changed, 88 insertions(+), 64 deletions(-) diff --git a/include/mbgl/text/glyph_store.hpp b/include/mbgl/text/glyph_store.hpp index 9c874b31b0..66cf8e75b3 100644 --- a/include/mbgl/text/glyph_store.hpp +++ b/include/mbgl/text/glyph_store.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -30,17 +31,11 @@ public: void insert(uint32_t id, const SDFGlyph &glyph); const std::map &getMetrics() const; const std::map &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 &translate) const; + void lineWrap(Shaping &shaping, float lineHeight, float maxWidth, float horizontalAlign, + float verticalAlign, float justify) const; private: std::map bitmaps; diff --git a/src/renderer/symbol_bucket.cpp b/src/renderer/symbol_bucket.cpp index e5bd3a3e1a..4072cf0c61 100644 --- a/src/renderer/symbol_bucket.cpp +++ b/src/renderer/symbol_bucket.cpp @@ -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 &line, const Shaping anchors = {Anchor{float(line[0].x), float(line[0].y), 0, minScale}}; } - const vec2 origin = {0, 0}; + // TODO: figure out correct ascender height. + const vec2 origin = {0, -17}; for (Anchor &anchor : anchors) { diff --git a/src/text/glyph_store.cpp b/src/text/glyph_store.cpp index fa39902227..a28569d851 100644 --- a/src/text/glyph_store.cpp +++ b/src/text/glyph_store.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -30,91 +31,108 @@ const std::map &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 &translate) const { std::lock_guard 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 &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 &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); + } - if (alignment) { - alignHorizontally(shaping, metrics, lineStartIndex, lastSafeBreak - 1, alignment); + lineStartIndex = lastSafeBreak + 1; + lastSafeBreak = 0; + lengthBeforeCurrentLine += lineLength; + line++; } - lineStartIndex = lastSafeBreak + 1; - lastSafeBreak = 0; - lengthBeforeCurrentLine += lineLength; - line++; - } - if (shape.glyph == 32) { - lastSafeBreak = i; + 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) -- cgit v1.2.1