freetype/
outline.rs

1use core::marker::PhantomData;
2use core::slice;
3use libc::{c_char, c_short};
4use {ffi, Vector};
5
6#[derive(Copy, Clone)]
7pub enum Curve {
8    Line(Vector),
9    Bezier2(Vector, Vector),
10    Bezier3(Vector, Vector, Vector),
11}
12
13pub struct Outline<'a> {
14    raw: &'a ffi::FT_Outline
15}
16
17impl<'a> Outline<'a> {
18    pub unsafe fn from_raw(raw: &'a ffi::FT_Outline) -> Self { Outline { raw } }
19
20    pub fn points(&self) -> &'a [Vector] {
21        unsafe { slice::from_raw_parts(self.raw.points, self.raw.n_points as usize) }
22    }
23
24    pub fn tags(&self) -> &'a [c_char] {
25        unsafe { slice::from_raw_parts(self.raw.tags, self.raw.n_points as usize) }
26    }
27
28    pub fn contours(&self) -> &'a [c_short] {
29        unsafe { slice::from_raw_parts(self.raw.contours, self.raw.n_contours as usize) }
30    }
31
32    pub fn contours_iter(&self) -> ContourIterator<'a> {
33        unsafe { ContourIterator::from_raw(self.raw) }
34    }
35}
36
37const TAG_ONCURVE: c_char = 0x01;
38const TAG_BEZIER3: c_char = 0x02;
39
40pub struct CurveIterator<'a> {
41    start_point: *const Vector,
42    start_tag: *const c_char,
43    idx: isize,
44    length: isize,
45    marker: PhantomData<&'a ()>
46}
47
48impl<'a> CurveIterator<'a> {
49    pub unsafe fn from_raw(outline: &'a ffi::FT_Outline, start_idx: isize, end_idx: isize) -> Self {
50        CurveIterator {
51            start_point: outline.points.offset(start_idx),
52            start_tag: outline.tags.offset(start_idx),
53            idx: 0,
54            length: end_idx - start_idx + 1,
55            marker: PhantomData
56        }
57    }
58
59    pub fn start(&self) -> &'a Vector {
60        unsafe { &*self.start_point }
61    }
62
63    // Retrieves the point at offset i from the current point. Note that contours implicitly repeat their
64    // first point at the end.
65    unsafe fn pt(&self, i: isize) -> Vector {
66        if self.idx + i < self.length {
67            *self.start_point.offset(self.idx + i)
68        } else {
69            *self.start_point
70        }
71    }
72
73    unsafe fn tg(&self, i: isize) -> c_char {
74        if self.idx + i < self.length {
75            *self.start_tag.offset(self.idx + i)
76        } else {
77            *self.start_tag
78        }
79    }
80}
81
82impl<'a> Iterator for CurveIterator<'a> {
83    type Item = Curve;
84
85    fn next(&mut self) -> Option<Self::Item> {
86        if self.idx >= self.length {
87            None
88        } else {
89            unsafe {
90                let tag1 = self.tg(1);
91
92                let (shift, curve) = if (tag1 & TAG_ONCURVE) == TAG_ONCURVE {
93                    (1, Curve::Line(self.pt(1)))
94                } else if (tag1 & TAG_BEZIER3) == TAG_BEZIER3 {
95                    (3, Curve::Bezier3(self.pt(1), self.pt(2), self.pt(3)))
96                } else {
97                    // We are some kind of quadratic Bezier.
98                    // Quadratic Bezier curves have a special treatment in TTF outlines:
99                    // as an optimization, curves are often constructed from sequences
100                    // of off-curve control points. In this case, there are implied on-curve
101                    // points in between each pair of off-curve points.
102                    if (self.tg(2) & TAG_ONCURVE) == TAG_ONCURVE {
103                        (2, Curve::Bezier2(self.pt(1), self.pt(2)))
104                    } else {
105                        let pt = ffi::FT_Vector {
106                            x: (self.pt(1).x + self.pt(2).x) / 2,
107                            y: (self.pt(1).y + self.pt(2).y) / 2,
108                        };
109
110                        (1, Curve::Bezier2(self.pt(1), pt))
111                    }
112                };
113
114                self.idx += shift;
115                Some(curve)
116            }
117        }
118    }
119}
120
121pub struct ContourIterator<'a> {
122    outline: &'a ffi::FT_Outline,
123    contour_start: c_short,
124    contour_end_idx: *const c_short,
125    last_end_idx: *const c_short,
126}
127
128impl<'a> ContourIterator<'a> {
129    pub unsafe fn from_raw(outline: &'a ffi::FT_Outline) -> Self {
130        ContourIterator {
131            outline,
132            contour_start: 0,
133            contour_end_idx: outline.contours,
134            last_end_idx: outline.contours.offset(outline.n_contours as isize - 1),
135        }
136    }
137}
138
139impl<'a> Iterator for ContourIterator<'a> {
140    type Item = CurveIterator<'a>;
141
142    fn next(&mut self) -> Option<Self::Item> {
143        if self.contour_end_idx > self.last_end_idx {
144            None
145        } else {
146            unsafe {
147                let contour_end = *self.contour_end_idx;
148                let curves = CurveIterator::from_raw(self.outline, self.contour_start as isize,
149                                                     contour_end as isize);
150                self.contour_start = contour_end + 1;
151                self.contour_end_idx = self.contour_end_idx.offset(1);
152
153                Some(curves)
154            }
155        }
156    }
157}