Skip to content

Commit 79ce25c

Browse files
committed
Fix PistonDevelopers#199 - properly iterate through contours starting with off-curve point
1 parent 18f1e92 commit 79ce25c

File tree

1 file changed

+54
-25
lines changed

1 file changed

+54
-25
lines changed

src/outline.rs

Lines changed: 54 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,19 @@ impl<'a> Outline<'a> {
4646
}
4747
}
4848

49+
const TAG_MASK: c_char = 0x03;
4950
const TAG_ONCURVE: c_char = 0x01;
51+
const TAG_BEZIER2: c_char = 0x00;
5052
const TAG_BEZIER3: c_char = 0x02;
5153

54+
#[inline]
55+
fn middle_point(pt1: Vector, pt2: Vector) -> Vector {
56+
ffi::FT_Vector {
57+
x: (pt1.x + pt2.x) / 2,
58+
y: (pt1.y + pt2.y) / 2,
59+
}
60+
}
61+
5262
pub struct CurveIterator<'a> {
5363
start_point: *const Vector,
5464
start_tag: *const c_char,
@@ -64,15 +74,27 @@ impl<'a> CurveIterator<'a> {
6474
CurveIterator {
6575
start_point: outline.points.offset(start_idx),
6676
start_tag: outline.tags.offset(start_idx),
67-
idx: 0,
77+
// If the first tag is off-curve, start at -1 and return last or interpolated point as start()
78+
idx: if *outline.tags.offset(start_idx) & TAG_MASK == TAG_BEZIER2 { -1 } else { 0 },
6879
length: end_idx - start_idx + 1,
6980
marker: PhantomData
7081
}
7182
}
7283

73-
pub fn start(&self) -> &'a Vector {
84+
pub fn start(&self) -> Vector {
7485
unsafe {
75-
&*self.start_point
86+
// Check if starting point is off-curve, use last point in that case
87+
if *self.start_tag & TAG_MASK == TAG_BEZIER2 {
88+
match *self.start_tag.offset(self.length - 1) {
89+
TAG_ONCURVE => *self.start_point.offset(self.length - 1),
90+
// If last point is also off-curve, then interpolate
91+
TAG_BEZIER2 => middle_point(*self.start_point.offset(self.length - 1),
92+
*self.start_point),
93+
_ => panic!("Unexpected curve tag"),
94+
}
95+
} else {
96+
*self.start_point
97+
}
7698
}
7799
}
78100

@@ -103,28 +125,35 @@ impl<'a> Iterator for CurveIterator<'a> {
103125
None
104126
} else {
105127
unsafe {
106-
let tag1 = self.tg(1);
107-
108-
let (shift, curve) = if (tag1 & TAG_ONCURVE) == TAG_ONCURVE {
109-
(1, Curve::Line(self.pt(1)))
110-
} else if (tag1 & TAG_BEZIER3) == TAG_BEZIER3 {
111-
(3, Curve::Bezier3(self.pt(1), self.pt(2), self.pt(3)))
112-
} else {
113-
// We are some kind of quadratic Bezier.
114-
// Quadratic Bezier curves have a special treatment in TTF outlines:
115-
// as an optimization, curves are often constructed from sequences
116-
// of off-curve control points. In this case, there are implied on-curve
117-
// points in between each pair of off-curve points.
118-
if (self.tg(2) & TAG_ONCURVE) == TAG_ONCURVE {
119-
(2, Curve::Bezier2(self.pt(1), self.pt(2)))
120-
} else {
121-
let pt = ffi::FT_Vector {
122-
x: (self.pt(1).x + self.pt(2).x) / 2,
123-
y: (self.pt(1).y + self.pt(2).y) / 2,
124-
};
125-
126-
(1, Curve::Bezier2(self.pt(1), pt))
127-
}
128+
let (shift, curve) =
129+
match self.tg(1) {
130+
TAG_ONCURVE => (1, Curve::Line(self.pt(1))),
131+
TAG_BEZIER2 => {
132+
// We are some kind of quadratic Bezier.
133+
// Quadratic Bezier curves have a special treatment in TTF outlines:
134+
// as an optimization, curves are often constructed from sequences
135+
// of off-curve control points. In this case, there are implied on-curve
136+
// points in between each pair of off-curve points.
137+
138+
// If we are at last point and checking first point (in circular fashion),
139+
// and it's off-curve, then stop iterating. The last point was already
140+
// reported by start() method.
141+
if self.idx == self.length - 1 {
142+
return None;
143+
}
144+
145+
match self.tg(2) {
146+
TAG_ONCURVE => (2, Curve::Bezier2(self.pt(1), self.pt(2))),
147+
TAG_BEZIER2 => (1, Curve::Bezier2(self.pt(1), middle_point(self.pt(1), self.pt(2)))),
148+
_ => panic!("Unexpected curve tag"),
149+
}
150+
},
151+
TAG_BEZIER3 => {
152+
debug_assert!(self.tg(2) == TAG_BEZIER3);
153+
debug_assert!(self.tg(3) == TAG_ONCURVE);
154+
(3, Curve::Bezier3(self.pt(1), self.pt(2), self.pt(3)))
155+
},
156+
_ => panic!("Unexpected curve tag"),
128157
};
129158

130159
self.idx += shift;

0 commit comments

Comments
 (0)