1 #include <mbgl/geometry/line_atlas.hpp>
2 #include <mbgl/gl/context.hpp>
3 #include <mbgl/util/logging.hpp>
4 #include <mbgl/util/platform.hpp>
5 
6 #include <boost/functional/hash.hpp>
7 
8 #include <sstream>
9 #include <cmath>
10 
11 namespace mbgl {
12 
LineAtlas(const Size size)13 LineAtlas::LineAtlas(const Size size)
14     : image(size),
15       dirty(true) {
16 }
17 
18 LineAtlas::~LineAtlas() = default;
19 
getDashPosition(const std::vector<float> & dasharray,LinePatternCap patternCap)20 LinePatternPos LineAtlas::getDashPosition(const std::vector<float>& dasharray,
21                                           LinePatternCap patternCap) {
22     size_t key = patternCap == LinePatternCap::Round ? std::numeric_limits<size_t>::min()
23                                                      : std::numeric_limits<size_t>::max();
24     for (const float part : dasharray) {
25         boost::hash_combine<float>(key, part);
26     }
27 
28     // Note: We're not handling hash collisions here.
29     const auto it = positions.find(key);
30     if (it == positions.end()) {
31         auto inserted = positions.emplace(key, addDash(dasharray, patternCap));
32         assert(inserted.second);
33         return inserted.first->second;
34     } else {
35         return it->second;
36     }
37 }
38 
addDash(const std::vector<float> & dasharray,LinePatternCap patternCap)39 LinePatternPos LineAtlas::addDash(const std::vector<float>& dasharray, LinePatternCap patternCap) {
40     const uint8_t n = patternCap == LinePatternCap::Round ? 7 : 0;
41     const uint8_t dashheight = 2 * n + 1;
42     const uint8_t offset = 128;
43 
44     if (dasharray.size() < 2) {
45         return LinePatternPos();
46     }
47 
48     if (nextRow + dashheight > image.size.height) {
49         Log::Warning(Event::OpenGL, "line atlas bitmap overflow");
50         return LinePatternPos();
51     }
52 
53     float length = 0;
54     for (const float part : dasharray) {
55         length += part;
56     }
57 
58     float stretch = image.size.width / length;
59     float halfWidth = stretch * 0.5;
60     // If dasharray has an odd length, both the first and last parts
61     // are dashes and should be joined seamlessly.
62     bool oddLength = dasharray.size() % 2 == 1;
63 
64     for (int y = -n; y <= n; y++) {
65         int row = nextRow + n + y;
66         int index = image.size.width * row;
67 
68         float left = 0;
69         float right = dasharray[0];
70         unsigned int partIndex = 1;
71 
72         if (oddLength) {
73             left -= dasharray.back();
74         }
75 
76         for (uint32_t x = 0; x < image.size.width; x++) {
77 
78             while (right < x / stretch) {
79                 left = right;
80                 if (partIndex >= dasharray.size()) {
81                     return LinePatternPos();
82                 }
83                 right = right + dasharray[partIndex];
84 
85                 if (oddLength && partIndex == dasharray.size() - 1) {
86                     right += dasharray.front();
87                 }
88 
89                 partIndex++;
90             }
91 
92             float distLeft = fabs(x - left * stretch);
93             float distRight = fabs(x - right * stretch);
94             float dist = fmin(distLeft, distRight);
95             bool inside = (partIndex % 2) == 1;
96             int signedDistance;
97 
98             if (patternCap == LinePatternCap::Round) {
99                 float distMiddle = n ? (float)y / n * (halfWidth + 1) : 0;
100                 if (inside) {
101                     float distEdge = halfWidth - fabs(distMiddle);
102                     signedDistance = sqrt(dist * dist + distEdge * distEdge);
103                 } else {
104                     signedDistance = halfWidth - sqrt(dist * dist + distMiddle * distMiddle);
105                 }
106 
107             } else {
108                 signedDistance = int((inside ? 1 : -1) * dist);
109             }
110 
111             image.data[index + x] = fmax(0, fmin(255, signedDistance + offset));
112         }
113     }
114 
115     LinePatternPos position;
116     position.y = (0.5 + nextRow + n) / image.size.height;
117     position.height = (2.0 * n) / image.size.height;
118     position.width = length;
119 
120     nextRow += dashheight;
121 
122     dirty = true;
123 
124     return position;
125 }
126 
getSize() const127 Size LineAtlas::getSize() const {
128     return image.size;
129 }
130 
upload(gl::Context & context,gl::TextureUnit unit)131 void LineAtlas::upload(gl::Context& context, gl::TextureUnit unit) {
132     if (!texture) {
133         texture = context.createTexture(image, unit);
134     } else if (dirty) {
135         context.updateTexture(*texture, image, unit);
136     }
137 
138     dirty = false;
139 }
140 
bind(gl::Context & context,gl::TextureUnit unit)141 void LineAtlas::bind(gl::Context& context, gl::TextureUnit unit) {
142     upload(context, unit);
143     context.bindTexture(*texture, unit, gl::TextureFilter::Linear, gl::TextureMipMap::No,
144                         gl::TextureWrap::Repeat, gl::TextureWrap::Clamp);
145 }
146 
147 } // namespace mbgl
148