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