6
6
#include < mbgl/util/logging.hpp>
7
7
#include < mbgl/util/platform.hpp>
8
8
9
- #include < cmath>
10
-
11
9
namespace mbgl {
12
10
namespace {
13
11
@@ -20,12 +18,112 @@ size_t getDashPatternHash(const std::vector<float>& dasharray, const LinePattern
20
18
return key;
21
19
}
22
20
21
+ std::vector<DashRange> getDashRanges (const std::vector<float >& dasharray, float stretch) {
22
+ // If dasharray has an odd length, both the first and last parts
23
+ // are dashes and should be joined seamlessly.
24
+ const bool oddDashArray = dasharray.size () % 2 == 1 ;
25
+
26
+ float left = oddDashArray ? -dasharray.back () * stretch : 0 .0f ;
27
+ float right = dasharray.front () * stretch;
28
+ bool isDash = true ;
29
+
30
+ std::vector<DashRange> ranges;
31
+ ranges.push_back ({left, right, isDash, dasharray.front () == 0 .0f });
32
+
33
+ float currentDashLength = dasharray.front ();
34
+ for (size_t i = 1 ; i < dasharray.size (); ++i) {
35
+ isDash = !isDash;
36
+
37
+ const float dashLength = dasharray[i];
38
+ left = currentDashLength * stretch;
39
+ currentDashLength += dashLength;
40
+ right = currentDashLength * stretch;
41
+
42
+ ranges.push_back ({left, right, isDash, dashLength == 0 .0f });
43
+ }
44
+
45
+ return ranges;
46
+ }
47
+
48
+ void addRoundDash (const std::vector<DashRange>& ranges, const int32_t yOffset, float stretch, const int n, AlphaImage& image) {
49
+ const float halfStretch = stretch * 0 .5f ;
50
+
51
+ for (int y = -n; y <= n; y++) {
52
+ int row = yOffset + n + y;
53
+ int index = image.size .width * row;
54
+ int currIndex = 0 ;
55
+ DashRange range = ranges[currIndex];
56
+
57
+ for (uint32_t x = 0 ; x < image.size .width ; x++) {
58
+ if (x / range.right > 1 .0f ) { range = ranges[++currIndex]; }
59
+
60
+ float distLeft = std::fabsf (x - range.left );
61
+ float distRight = std::fabsf (x - range.right );
62
+ float minDist = std::fminf (distLeft, distRight);
63
+ float signedDistance;
64
+
65
+ float distMiddle = static_cast <float >(y) / n * (halfStretch + 1 .0f );
66
+ if (range.isDash ) {
67
+ float distEdge = halfStretch - std::fabsf (distMiddle);
68
+ signedDistance = std::sqrtf (minDist * minDist + distEdge * distEdge);
69
+ } else {
70
+ signedDistance = halfStretch - std::sqrtf (minDist * minDist + distMiddle * distMiddle);
71
+ }
72
+
73
+ image.data [index + x] = static_cast <uint8_t >(std::fmaxf (0 .0f , std::fminf (255 .0f , signedDistance + 128 .0f )));
74
+ }
75
+ }
76
+ }
77
+
78
+ void addRegularDash (std::vector<DashRange>& ranges, const int32_t yOffset, AlphaImage& image) {
79
+ // Collapse any zero-length range
80
+ for (auto it = ranges.begin (); it != ranges.end ();) {
81
+ if (it->isZeroLength ) {
82
+ ranges.erase (it);
83
+ } else { ++it; }
84
+ }
85
+
86
+ if (ranges.size () >= 2 ) {
87
+ // Collapse neighbouring same-type parts into a single part
88
+ for (auto curr = ranges.begin (), next = ranges.begin () + 1 ; curr != ranges.end () && next >= ranges.end ();) {
89
+ if (next->isDash == curr->isDash ) {
90
+ next->left = curr->left ;
91
+ ranges.erase (curr);
92
+ } else {
93
+ ++curr;
94
+ ++next;
95
+ }
96
+ }
97
+ }
98
+
99
+ DashRange& first = ranges.front ();
100
+ DashRange& last = ranges.back ();
101
+ if (first.isDash == last.isDash ) {
102
+ first.left = last.left - image.size .width ;
103
+ last.right = first.right + image.size .width ;
104
+ }
105
+
106
+ int index = image.size .width * yOffset;
107
+ int currIndex = 0 ;
108
+ DashRange range = ranges[currIndex];
109
+
110
+ for (uint32_t x = 0 ; x < image.size .width ; ++x) {
111
+ if (x / range.right > 1 .0f ) { range = ranges[++currIndex]; }
112
+
113
+ float distLeft = std::fabsf (x - range.left );
114
+ float distRight = std::fabsf (x - range.right );
115
+ float minDist = std::fminf (distLeft, distRight);
116
+ float signedDistance = range.isDash ? minDist : -minDist;
117
+
118
+ image.data [index + x] = static_cast <uint8_t >(std::fmaxf (0 .0f , std::fminf (255 .0f , signedDistance + 128 .0f )));
119
+ }
120
+ }
121
+
23
122
LinePatternPos addDashPattern (AlphaImage& image,
24
123
const int32_t yOffset,
25
124
const std::vector<float >& dasharray,
26
125
const LinePatternCap patternCap) {
27
126
const uint8_t n = patternCap == LinePatternCap::Round ? 7 : 0 ;
28
- constexpr const uint8_t offset = 128 ;
29
127
30
128
if (dasharray.size () < 2 ) {
31
129
Log::Warning (Event::ParseStyle, " line dasharray requires at least two elements" );
@@ -38,64 +136,17 @@ LinePatternPos addDashPattern(AlphaImage& image,
38
136
}
39
137
40
138
float stretch = image.size .width / length;
41
- float halfWidth = stretch * 0.5 ;
42
- // If dasharray has an odd length, both the first and last parts
43
- // are dashes and should be joined seamlessly.
44
- bool oddLength = dasharray.size () % 2 == 1 ;
139
+ std::vector<DashRange> ranges = getDashRanges (dasharray, stretch);
45
140
46
- for (int y = -n; y <= n; y++) {
47
- int row = yOffset + n + y;
48
- int index = image.size .width * row;
49
-
50
- float left = 0 ;
51
- float right = dasharray[0 ];
52
- unsigned int partIndex = 1 ;
53
-
54
- if (oddLength) {
55
- left -= dasharray.back ();
56
- }
57
-
58
- for (uint32_t x = 0 ; x < image.size .width ; x++) {
59
- while (right < x / stretch) {
60
- left = right;
61
- if (partIndex >= dasharray.size ()) {
62
- return LinePatternPos ();
63
- }
64
- right = right + dasharray[partIndex];
65
-
66
- if (oddLength && partIndex == dasharray.size () - 1 ) {
67
- right += dasharray.front ();
68
- }
69
-
70
- partIndex++;
71
- }
72
-
73
- float distLeft = fabs (x - left * stretch);
74
- float distRight = fabs (x - right * stretch);
75
- float dist = fmin (distLeft, distRight);
76
- bool inside = (partIndex % 2 ) == 1 ;
77
- int signedDistance;
78
-
79
- if (patternCap == LinePatternCap::Round) {
80
- float distMiddle = n ? (float )y / n * (halfWidth + 1 ) : 0 ;
81
- if (inside) {
82
- float distEdge = halfWidth - fabs (distMiddle);
83
- signedDistance = sqrt (dist * dist + distEdge * distEdge);
84
- } else {
85
- signedDistance = halfWidth - sqrt (dist * dist + distMiddle * distMiddle);
86
- }
87
-
88
- } else {
89
- signedDistance = int ((inside ? 1 : -1 ) * dist);
90
- }
91
-
92
- image.data [index + x] = fmax (0 , fmin (255 , signedDistance + offset));
93
- }
141
+ if (patternCap == LinePatternCap::Round) {
142
+ addRoundDash (ranges, yOffset, stretch, n, image);
143
+ } else {
144
+ addRegularDash (ranges, yOffset, image);
94
145
}
95
146
96
147
LinePatternPos position;
97
- position.y = (0.5 + yOffset + n) / image.size .height ;
98
- position.height = (2.0 * n + 1 ) / image.size .height ;
148
+ position.y = (0 .5f + yOffset + n) / image.size .height ;
149
+ position.height = (2 .0f * n + 1 ) / image.size .height ;
99
150
position.width = length;
100
151
101
152
return position;
0 commit comments