bool SymbolBucket::needsDependencies(const GeometryTileLayer& layer, const FilterExpression& filter, GlyphStore& glyphStore, Sprite& sprite) { const bool has_text = !layout.text.field.empty() && !layout.text.font.empty(); const bool has_icon = !layout.icon.image.empty(); if (!has_text && !has_icon) { return false; } // Determine and load glyph ranges std::set<GlyphRange> ranges; for (std::size_t i = 0; i < layer.featureCount(); i++) { auto feature = layer.getFeature(i); GeometryTileFeatureExtractor extractor(*feature); if (!evaluate(filter, extractor)) continue; SymbolFeature ft; auto getValue = [&feature](const std::string& key) -> std::string { auto value = feature->getValue(key); return value ? toString(*value) : std::string(); }; if (has_text) { std::string u8string = util::replaceTokens(layout.text.field, getValue); if (layout.text.transform == TextTransformType::Uppercase) { u8string = platform::uppercase(u8string); } else if (layout.text.transform == TextTransformType::Lowercase) { u8string = platform::lowercase(u8string); } ft.label = util::utf8_to_utf32::convert(u8string); if (ft.label.size()) { // Loop through all characters of this text and collect unique codepoints. for (char32_t chr : ft.label) { ranges.insert(getGlyphRange(chr)); } } } if (has_icon) { ft.sprite = util::replaceTokens(layout.icon.image, getValue); } if (ft.label.length() || ft.sprite.length()) { auto &multiline = ft.geometry; GeometryCollection geometryCollection = feature->getGeometries(); for (auto& line : geometryCollection) { multiline.emplace_back(); for (auto& point : line) { multiline.back().emplace_back(point.x, point.y); } } features.push_back(std::move(ft)); } } if (layout.placement == PlacementType::Line) { util::mergeLines(features); } if (glyphStore.requestGlyphRangesIfNeeded(layout.text.font, ranges)) { return true; } if (!sprite.isLoaded()) { return true; } return false; }
void SymbolBucket::addFeatures(uintptr_t tileUID, SpriteAtlas& spriteAtlas, GlyphAtlas& glyphAtlas, GlyphStore& glyphStore) { float horizontalAlign = 0.5; float verticalAlign = 0.5; switch (layout.textAnchor) { case TextAnchorType::Top: case TextAnchorType::Bottom: case TextAnchorType::Center: break; case TextAnchorType::Right: case TextAnchorType::TopRight: case TextAnchorType::BottomRight: horizontalAlign = 1; break; case TextAnchorType::Left: case TextAnchorType::TopLeft: case TextAnchorType::BottomLeft: horizontalAlign = 0; break; } switch (layout.textAnchor) { case TextAnchorType::Left: case TextAnchorType::Right: case TextAnchorType::Center: break; case TextAnchorType::Bottom: case TextAnchorType::BottomLeft: case TextAnchorType::BottomRight: verticalAlign = 1; break; case TextAnchorType::Top: case TextAnchorType::TopLeft: case TextAnchorType::TopRight: verticalAlign = 0; break; } const float justify = layout.textJustify == TextJustifyType::Right ? 1 : layout.textJustify == TextJustifyType::Left ? 0 : 0.5; auto glyphSet = glyphStore.getGlyphSet(layout.textFont); for (const auto& feature : features) { if (feature.geometry.empty()) continue; Shaping shapedText; PositionedIcon shapedIcon; GlyphPositions face; // if feature has text, shape the text if (feature.label.length()) { shapedText = glyphSet->getShaping( /* string */ feature.label, /* maxWidth: ems */ layout.symbolPlacement != SymbolPlacementType::Line ? layout.textMaxWidth * 24 : 0, /* lineHeight: ems */ layout.textLineHeight * 24, /* horizontalAlign */ horizontalAlign, /* verticalAlign */ verticalAlign, /* justify */ justify, /* spacing: ems */ layout.textLetterSpacing * 24, /* translate */ Point<float>(layout.textOffset.value[0], layout.textOffset.value[1])); // Add the glyphs we need for this label to the glyph atlas. if (shapedText) { glyphAtlas.addGlyphs(tileUID, feature.label, layout.textFont, **glyphSet, face); } } // if feature has icon, get sprite atlas position if (feature.sprite.length()) { auto image = spriteAtlas.getImage(feature.sprite, false); if (image) { shapedIcon = shapeIcon(*image, layout); assert((*image).spriteImage); if ((*image).spriteImage->sdf) { sdfIcons = true; } if ((*image).relativePixelRatio != 1.0f) { iconsNeedLinear = true; } } } // if either shapedText or icon position is present, add the feature if (shapedText || shapedIcon) { addFeature(feature.geometry, shapedText, shapedIcon, face, feature.index); } } features.clear(); }
void SymbolBucket::addFeatures(uintptr_t tileUID, SpriteAtlas& spriteAtlas, GlyphAtlas& glyphAtlas, GlyphStore& glyphStore) { float horizontalAlign = 0.5; float verticalAlign = 0.5; switch (layout.text.anchor) { case TextAnchorType::Top: case TextAnchorType::Bottom: case TextAnchorType::Center: break; case TextAnchorType::Right: case TextAnchorType::TopRight: case TextAnchorType::BottomRight: horizontalAlign = 1; break; case TextAnchorType::Left: case TextAnchorType::TopLeft: case TextAnchorType::BottomLeft: horizontalAlign = 0; break; } switch (layout.text.anchor) { case TextAnchorType::Left: case TextAnchorType::Right: case TextAnchorType::Center: break; case TextAnchorType::Bottom: case TextAnchorType::BottomLeft: case TextAnchorType::BottomRight: verticalAlign = 1; break; case TextAnchorType::Top: case TextAnchorType::TopLeft: case TextAnchorType::TopRight: verticalAlign = 0; break; } const float justify = layout.text.justify == TextJustifyType::Right ? 1 : layout.text.justify == TextJustifyType::Left ? 0 : 0.5; auto fontStack = glyphStore.getFontStack(layout.text.font); for (const auto& feature : features) { if (!feature.geometry.size()) continue; Shaping shapedText; PositionedIcon shapedIcon; GlyphPositions face; // if feature has text, shape the text if (feature.label.length()) { shapedText = fontStack->getShaping( /* string */ feature.label, /* maxWidth: ems */ layout.placement != PlacementType::Line ? layout.text.max_width * 24 : 0, /* lineHeight: ems */ layout.text.line_height * 24, /* horizontalAlign */ horizontalAlign, /* verticalAlign */ verticalAlign, /* justify */ justify, /* spacing: ems */ layout.text.letter_spacing * 24, /* translate */ vec2<float>(layout.text.offset[0], layout.text.offset[1])); // Add the glyphs we need for this label to the glyph atlas. if (shapedText) { glyphAtlas.addGlyphs(tileUID, feature.label, layout.text.font, **fontStack, face); } } // if feature has icon, get sprite atlas position if (feature.sprite.length()) { auto image = spriteAtlas.getImage(feature.sprite, false); if (image.pos.hasArea() && image.texture) { shapedIcon = shapeIcon(image.pos, layout); assert(image.texture); if (image.texture->sdf) { sdfIcons = true; } } } // if either shapedText or icon position is present, add the feature if (shapedText || shapedIcon) { addFeature(feature.geometry, shapedText, shapedIcon, face); } } features.clear(); placeFeatures(true); }