Пример #1
0
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});
        }
    }
}
Пример #2
0
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});
        }
    }
}
Пример #3
0
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});
    }
}
Пример #4
0
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;
}
Пример #5
0
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;
}
Пример #6
0
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();
}