summaryrefslogtreecommitdiff
path: root/src/renderer/text_bucket.cpp
blob: 165d636f53beac9b0a65040b47a836eb11d90254 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#include <llmr/renderer/text_bucket.hpp>
#include <llmr/geometry/text_buffer.hpp>
#include <llmr/geometry/elements_buffer.hpp>
#include <llmr/geometry/geometry.hpp>

#include <llmr/renderer/painter.hpp>
#include <llmr/style/style.hpp>
#include <llmr/map/vector_tile.hpp>
#include <llmr/text/placement.hpp>
#include <llmr/text/glyph_store.hpp>
#include <llmr/util/constants.hpp>

#include <llmr/util/math.hpp>
#include <llmr/platform/gl.hpp>

#include <iostream>

#include <cassert>

using namespace llmr;

TextBucket::TextBucket(
    TextVertexBuffer &vertexBuffer,
    TriangleElementsBuffer &triangleElementsBuffer,
    const StyleBucketText &properties, Placement &placement)
    : properties(properties),
      vertexBuffer(vertexBuffer),
      triangleElementsBuffer(triangleElementsBuffer),
      placement(placement),
      vertex_start(vertexBuffer.index()),
      triangle_elements_start(triangleElementsBuffer.index()) {}

void TextBucket::addGlyphs(const PlacedGlyphs &glyphs, float placementZoom,
                           PlacementRange placementRange, float zoom) {
    placementZoom += zoom;

    for (const PlacedGlyph &glyph : glyphs) {
        const auto &tl = glyph.tl;
        const auto &tr = glyph.tr;
        const auto &bl = glyph.bl;
        const auto &br = glyph.br;
        const auto &tex = glyph.tex;
        const auto &angle = glyph.angle;

        float minZoom = util::max(
            static_cast<float>(zoom + log(glyph.glyphBox.minScale) / log(2)),
            placementZoom);
        float maxZoom = util::min(
            static_cast<float>(zoom + log(glyph.glyphBox.maxScale) / log(2)),
            25.0f);
        const auto &glyphAnchor = glyph.glyphBox.anchor;

        if (maxZoom <= minZoom)
            continue;

        // Lower min zoom so that while fading out the label
        // it can be shown outside of collision-free zoom levels
        if (minZoom == placementZoom) {
            minZoom = 0;
        }

        const int glyph_vertex_length = 4;

        if (!triangleGroups.size() ||
            (triangleGroups.back().vertex_length + glyph_vertex_length >
             65535)) {
            // Move to a new group because the old one can't hold the geometry.
            triangleGroups.emplace_back();
        }

        // We're generating triangle fans, so we always start with the first
        // coordinate in this polygon.
        triangle_group_type &triangleGroup = triangleGroups.back();
        uint32_t triangleIndex = triangleGroup.vertex_length;

        // coordinates (2 triangles)
        vertexBuffer.add(glyphAnchor.x, glyphAnchor.y, tl.x, tl.y, tex.x,
                          tex.y, angle, minZoom, placementRange, maxZoom,
                          placementZoom);
        vertexBuffer.add(glyphAnchor.x, glyphAnchor.y, tr.x, tr.y,
                          tex.x + tex.w, tex.y, angle, minZoom, placementRange,
                          maxZoom, placementZoom);
        vertexBuffer.add(glyphAnchor.x, glyphAnchor.y, bl.x, bl.y, tex.x,
                          tex.y + tex.h, angle, minZoom, placementRange,
                          maxZoom, placementZoom);
        vertexBuffer.add(glyphAnchor.x, glyphAnchor.y, br.x, br.y,
                          tex.x + tex.w, tex.y + tex.h, angle, minZoom,
                          placementRange, maxZoom, placementZoom);

        // add the two triangles, referencing the four coordinates we just
        // inserted.
        triangleElementsBuffer.add(triangleIndex + 0, triangleIndex + 1,
                                    triangleIndex + 2);
        triangleElementsBuffer.add(triangleIndex + 1, triangleIndex + 2,
                                    triangleIndex + 3);

        triangleGroup.vertex_length += glyph_vertex_length;
        triangleGroup.elements_length += 2;
    }
};

void TextBucket::addFeature(const pbf &geom_pbf,
                            const GlyphPositions &face,
                            const Shaping &shaping) {
    // Decode all lines.
    std::vector<Coordinate> line;
    Geometry::command cmd;

    Coordinate coord;
    pbf geom(geom_pbf);
    Geometry geometry(geom);
    int32_t x, y;
    while ((cmd = geometry.next(x, y)) != Geometry::end) {
        if (cmd == Geometry::move_to) {
            if (!line.empty()) {
                placement.addFeature(*this, line, properties, face, shaping);
                line.clear();
            }
        }
        line.emplace_back(x, y);
    }
    if (line.size()) {
        placement.addFeature(*this, line, properties, face, shaping);
    }
}

void TextBucket::render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc,
                        const Tile::ID &id) {
    painter.renderText(*this, layer_desc, id);
}

bool TextBucket::hasData() const {
    return !triangleGroups.empty();
}

void TextBucket::drawGlyphs(TextShader &shader) {
    char *vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize);
    char *elements_index =
        BUFFER_OFFSET(triangle_elements_start * triangleElementsBuffer.itemSize);
    for (triangle_group_type &group : triangleGroups) {
        group.array.bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index);
        glDrawElements(GL_TRIANGLES, group.elements_length * 3, GL_UNSIGNED_SHORT, elements_index);
        vertex_index += group.vertex_length * vertexBuffer.itemSize;
        elements_index += group.elements_length * triangleElementsBuffer.itemSize;
    }
}