void TileParser::addGlyph(uint64_t tileid, const std::string stackname, const std::u32string &string, const FontStack &fontStack, GlyphAtlas &glyphAtlas, GlyphPositions &face) { const std::map<uint32_t, SDFGlyph> &sdfs = fontStack.getSDFs(); // Loop through all characters and add glyph to atlas, positions. for (uint32_t chr : string) { auto sdf_it = sdfs.find(chr); if (sdf_it != sdfs.end()) { const SDFGlyph& sdf = sdf_it->second; const Rect<uint16_t> rect = glyphAtlas.addGlyph(tileid, stackname, sdf); face.emplace(chr, Glyph{rect, sdf.metrics}); } } }
void GlyphAtlas::addGlyphs(uint64_t tileid, std::u32string const& text, std::string const& stackname, FontStack const& fontStack, GlyphPositions & face) { std::lock_guard<std::mutex> lock(mtx); std::map<uint32_t, SDFGlyph> const& sdfs = fontStack.getSDFs(); for (uint32_t chr : text) { auto sdf_it = sdfs.find(chr); if (sdf_it != sdfs.end()) { SDFGlyph const& sdf = sdf_it->second; Rect<uint16_t> rect = addGlyph_impl(tileid, stackname, sdf); face.emplace(chr, Glyph{rect, sdf.metrics}); } } }
void GlyphAtlas::addGlyphs(uintptr_t tileUID, const std::u32string& text, const std::string& stackName, const FontStack& fontStack, GlyphPositions& face) { std::lock_guard<std::mutex> lock(mtx); const std::map<uint32_t, SDFGlyph>& sdfs = fontStack.getSDFs(); for (uint32_t chr : text) { auto sdf_it = sdfs.find(chr); if (sdf_it == sdfs.end()) { continue; } const SDFGlyph& sdf = sdf_it->second; Rect<uint16_t> rect = addGlyph(tileUID, stackName, sdf); face.emplace(chr, Glyph{rect, sdf.metrics}); } }
SymbolQuads getGlyphQuads(Anchor& anchor, const Shaping& shapedText, const float boxScale, const GeometryCoordinates& line, const SymbolLayoutProperties& layout, const bool alongLine, const GlyphPositions& face) { const float textRotate = layout.text.rotate * util::DEG2RAD; const bool keepUpright = layout.text.keepUpright; SymbolQuads quads; for (const PositionedGlyph &positionedGlyph: shapedText.positionedGlyphs) { auto face_it = face.find(positionedGlyph.glyph); if (face_it == face.end()) continue; const Glyph &glyph = face_it->second; const Rect<uint16_t> &rect = glyph.rect; if (!glyph) continue; if (!rect.hasArea()) continue; const float centerX = (positionedGlyph.x + glyph.metrics.advance / 2.0f) * boxScale; GlyphInstances glyphInstances; if (alongLine) { getSegmentGlyphs(std::back_inserter(glyphInstances), anchor, centerX, line, anchor.segment, true); if (keepUpright) getSegmentGlyphs(std::back_inserter(glyphInstances), anchor, centerX, line, anchor.segment, false); } else { glyphInstances.emplace_back(GlyphInstance{anchor}); } // The rects have an addditional buffer that is not included in their size; const float glyphPadding = 1.0f; const float rectBuffer = 3.0f + glyphPadding; const float x1 = positionedGlyph.x + glyph.metrics.left - rectBuffer; const float y1 = positionedGlyph.y - glyph.metrics.top - rectBuffer; const float x2 = x1 + rect.w; const float y2 = y1 + rect.h; const vec2<float> otl{x1, y1}; const vec2<float> otr{x2, y1}; const vec2<float> obl{x1, y2}; const vec2<float> obr{x2, y2}; for (const GlyphInstance &instance : glyphInstances) { vec2<float> tl = otl; vec2<float> tr = otr; vec2<float> bl = obl; vec2<float> br = obr; const float angle = instance.angle + textRotate; if (angle) { // Compute the transformation matrix. float angle_sin = std::sin(angle); float angle_cos = std::cos(angle); std::array<float, 4> matrix = {{angle_cos, -angle_sin, angle_sin, angle_cos}}; tl = tl.matMul(matrix); tr = tr.matMul(matrix); bl = bl.matMul(matrix); br = br.matMul(matrix); } // Prevent label from extending past the end of the line const float glyphMinScale = std::max(instance.minScale, anchor.scale); const float glyphAngle = std::fmod((anchor.angle + textRotate + instance.offset + 2 * M_PI), (2 * M_PI)); quads.emplace_back(tl, tr, bl, br, rect, glyphAngle, instance.anchorPoint, glyphMinScale, instance.maxScale); } } return quads; }
Placement Placement::getGlyphs(Anchor &anchor, const vec2<float> &origin, const Shaping &shaping, const GlyphPositions &face, float boxScale, bool horizontal, const std::vector<Coordinate> &line, const StyleBucketSymbol &props) { const float maxAngle = props.text.max_angle * M_PI / 180; const float rotate = props.text.rotate * M_PI / 180; const float padding = props.text.padding; const bool alongLine = props.text.rotation_alignment != RotationAlignmentType::Viewport; const bool keepUpright = props.text.keep_upright; Placement placement; const uint32_t buffer = 3; for (const PositionedGlyph &shape : shaping) { auto face_it = face.find(shape.glyph); if (face_it == face.end()) continue; const Glyph &glyph = face_it->second; const Rect<uint16_t> &rect = glyph.rect; if (!glyph) continue; if (!rect) continue; const float x = (origin.x + shape.x + glyph.metrics.left - buffer + rect.w / 2) * boxScale; GlyphInstances glyphInstances; if (anchor.segment >= 0 && alongLine) { getSegmentGlyphs(std::back_inserter(glyphInstances), anchor, x, line, anchor.segment, 1, maxAngle); if (keepUpright) getSegmentGlyphs(std::back_inserter(glyphInstances), anchor, x, line, anchor.segment, -1, maxAngle); } else { glyphInstances.emplace_back(GlyphInstance{anchor}); } const float x1 = origin.x + shape.x + glyph.metrics.left - buffer; const float y1 = origin.y + shape.y - glyph.metrics.top - buffer; const float x2 = x1 + glyph.rect.w; const float y2 = y1 + glyph.rect.h; const vec2<float> otl{x1, y1}; const vec2<float> otr{x2, y1}; const vec2<float> obl{x1, y2}; const vec2<float> obr{x2, y2}; const CollisionRect obox{boxScale * x1, boxScale * y1, boxScale * x2, boxScale * y2}; for (const GlyphInstance &instance : glyphInstances) { vec2<float> tl = otl; vec2<float> tr = otr; vec2<float> bl = obl; vec2<float> br = obr; CollisionRect box = obox; // Clamp to -90/+90 degrees const float angle = instance.angle + rotate; if (angle) { // Compute the transformation matrix. float angle_sin = std::sin(angle); float angle_cos = std::cos(angle); std::array<float, 4> matrix = {{angle_cos, -angle_sin, angle_sin, angle_cos}}; tl = tl.matMul(matrix); tr = tr.matMul(matrix); bl = bl.matMul(matrix); br = br.matMul(matrix); } // Prevent label from extending past the end of the line const float glyphMinScale = std::max(instance.minScale, anchor.scale); // Remember the glyph for later insertion. placement.shapes.emplace_back( tl, tr, bl, br, rect, float(std::fmod((anchor.angle + rotate + instance.offset + 2 * M_PI), (2 * M_PI))), instance.anchor, glyphMinScale, instance.maxScale); if (!instance.offset) { // not a flipped glyph if (angle) { // Calculate the rotated glyph's bounding box offsets from the anchor point. box = CollisionRect{boxScale * util::min(tl.x, tr.x, bl.x, br.x), boxScale * util::min(tl.y, tr.y, bl.y, br.y), boxScale * util::max(tl.x, tr.x, bl.x, br.x), boxScale * util::max(tl.y, tr.y, bl.y, br.y)}; } placement.boxes.emplace_back(box, instance.anchor, glyphMinScale, instance.maxScale, padding); } } } // TODO avoid creating the boxes in the first place? if (horizontal) placement.boxes = {getMergedBoxes(placement.boxes, anchor)}; const float minPlacementScale = anchor.scale; placement.minScale = std::numeric_limits<float>::infinity(); for (const GlyphBox &box : placement.boxes) { placement.minScale = util::min(placement.minScale, box.minScale); } placement.minScale = util::max(minPlacementScale, Placement::globalMinScale); return placement; }
void SymbolLayout::prepare(const GlyphMap& glyphMap, const GlyphPositions& glyphPositions, const ImageMap& imageMap, const ImagePositions& imagePositions, const OverscaledTileID& tileID, const std::string& sourceID) { const bool textAlongLine = layout.get<TextRotationAlignment>() == AlignmentType::Map && layout.get<SymbolPlacement>() == SymbolPlacementType::Line; auto glyphMapIt = glyphMap.find(layout.get<TextFont>()); const Glyphs& glyphs = glyphMapIt != glyphMap.end() ? glyphMapIt->second : Glyphs(); auto glyphPositionsIt = glyphPositions.find(layout.get<TextFont>()); const GlyphPositionMap& glyphPositionMap = glyphPositionsIt != glyphPositions.end() ? glyphPositionsIt->second : GlyphPositionMap(); for (auto it = features.begin(); it != features.end(); ++it) { auto& feature = *it; if (feature.geometry.empty()) continue; std::pair<Shaping, Shaping> shapedTextOrientations; optional<PositionedIcon> shapedIcon; // if feature has text, shape the text if (feature.text) { auto applyShaping = [&] (const std::u16string& text, WritingModeType writingMode) { const float oneEm = 24.0f; const Shaping result = getShaping( /* string */ text, /* maxWidth: ems */ layout.get<SymbolPlacement>() != SymbolPlacementType::Line ? layout.evaluate<TextMaxWidth>(zoom, feature) * oneEm : 0, /* lineHeight: ems */ layout.get<TextLineHeight>() * oneEm, /* anchor */ layout.evaluate<TextAnchor>(zoom, feature), /* justify */ layout.evaluate<TextJustify>(zoom, feature), /* spacing: ems */ util::i18n::allowsLetterSpacing(*feature.text) ? layout.evaluate<TextLetterSpacing>(zoom, feature) * oneEm : 0.0f, /* translate */ Point<float>(layout.evaluate<TextOffset>(zoom, feature)[0] * oneEm, layout.evaluate<TextOffset>(zoom, feature)[1] * oneEm), /* verticalHeight */ oneEm, /* writingMode */ writingMode, /* bidirectional algorithm object */ bidi, /* glyphs */ glyphs); return result; }; shapedTextOrientations.first = applyShaping(*feature.text, WritingModeType::Horizontal); if (util::i18n::allowsVerticalWritingMode(*feature.text) && textAlongLine) { shapedTextOrientations.second = applyShaping(util::i18n::verticalizePunctuation(*feature.text), WritingModeType::Vertical); } } // if feature has icon, get sprite atlas position if (feature.icon) { auto image = imageMap.find(*feature.icon); if (image != imageMap.end()) { shapedIcon = PositionedIcon::shapeIcon( imagePositions.at(*feature.icon), layout.evaluate<IconOffset>(zoom, feature), layout.evaluate<IconAnchor>(zoom, feature), layout.evaluate<IconRotate>(zoom, feature) * util::DEG2RAD); if (image->second->sdf) { sdfIcons = true; } if (image->second->pixelRatio != pixelRatio) { iconsNeedLinear = true; } else if (layout.get<IconRotate>().constantOr(1) != 0) { iconsNeedLinear = true; } } } // if either shapedText or icon position is present, add the feature if (shapedTextOrientations.first || shapedIcon) { addFeature(std::distance(features.begin(), it), feature, shapedTextOrientations, shapedIcon, glyphPositionMap, tileID, sourceID); } feature.geometry.clear(); } compareText.clear(); }