Skip to main content

skia_safe/core/
contour_measure.rs

1use std::fmt;
2use std::marker::PhantomData;
3
4use skia_bindings::{
5    self as sb, SkContourMeasure, SkContourMeasureIter, SkContourMeasure_ForwardVerbIterator,
6    SkContourMeasure_VerbMeasure, SkRefCntBase,
7};
8
9use crate::{prelude::*, scalar, Matrix, Path, PathBuilder, PathVerb, Point, Vector};
10
11pub type ContourMeasure = RCHandle<SkContourMeasure>;
12unsafe_send_sync!(ContourMeasure);
13
14impl NativeRefCountedBase for SkContourMeasure {
15    type Base = SkRefCntBase;
16}
17
18bitflags! {
19    /// Flags that control what [`ContourMeasure::get_matrix()`] computes.
20    #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
21    pub struct MatrixFlags : u32 {
22        /// Compute the position component.
23        const GET_POSITION = sb::SkContourMeasure_MatrixFlags_kGetPosition_MatrixFlag as _;
24        /// Compute the tangent component.
25        const GET_TANGENT = sb::SkContourMeasure_MatrixFlags_kGetTangent_MatrixFlag as _;
26        /// Compute both position and tangent components.
27        const GET_POS_AND_TAN = Self::GET_POSITION.bits() | Self::GET_TANGENT.bits();
28    }
29}
30
31impl Default for MatrixFlags {
32    fn default() -> Self {
33        Self::GET_POS_AND_TAN
34    }
35}
36
37impl fmt::Debug for ContourMeasure {
38    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39        f.debug_struct("ContourMeasure")
40            .field("length", &self.length())
41            .field("is_closed", &self.is_closed())
42            .finish()
43    }
44}
45
46impl ContourMeasure {
47    /// Returns the length of the contour.
48    pub fn length(&self) -> scalar {
49        unsafe { sb::C_SkContourMeasure_length(self.native()) }
50    }
51
52    /// Pins `distance` to `0 <= distance <= length()`, then computes the corresponding
53    /// position and tangent.
54    ///
55    /// - `distance`: distance along the contour.
56    #[must_use]
57    pub fn pos_tan(&self, distance: scalar) -> Option<(Point, Vector)> {
58        let mut p = Point::default();
59        let mut v = Vector::default();
60        unsafe {
61            self.native()
62                .getPosTan(distance, p.native_mut(), v.native_mut())
63        }
64        .then_some((p, v))
65    }
66
67    #[must_use]
68    /// Pins `distance` to `0 <= distance <= length()`, then computes the corresponding
69    /// matrix (by calling [`Self::pos_tan()`]).
70    ///
71    /// Returns `None` if there is no path, or a zero-length path was specified.
72    ///
73    /// - `distance`: distance along the contour.
74    /// - `flags`: controls whether position, tangent, or both are computed.
75    pub fn get_matrix(
76        &self,
77        distance: scalar,
78        flags: impl Into<Option<MatrixFlags>>,
79    ) -> Option<Matrix> {
80        let mut m = Matrix::default();
81        unsafe {
82            self.native().getMatrix(
83                distance,
84                m.native_mut(),
85                // note: depending on the OS, different representation types are generated for
86                // MatrixFlags, so the try_into() is required, even though clippy complains about
87                // it.
88                #[allow(clippy::useless_conversion)]
89                flags.into().unwrap_or_default().bits().try_into().unwrap(),
90            )
91        }
92        .then_some(m)
93    }
94
95    #[deprecated(since = "0.94.0", note = "Use get_segment()")]
96    #[must_use]
97    /// Given a start and stop distance, appends the intervening segment(s) to `path_builder`.
98    ///
99    /// If the segment is zero-length, returns `false`; otherwise returns `true`.
100    /// `start_d` and `stop_d` are pinned to legal values (`0..length()`). If
101    /// `start_d > stop_d`, returns `false` and leaves `path_builder` untouched.
102    ///
103    /// Begins the segment with a `move_to` if `start_with_move_to` is `true`.
104    ///
105    /// - `start_d`: start distance along the contour.
106    /// - `stop_d`: stop distance along the contour.
107    /// - `path_builder`: destination that receives the segment.
108    /// - `start_with_move_to`: whether to begin with `move_to`.
109    pub fn segment(
110        &self,
111        start_d: scalar,
112        stop_d: scalar,
113        path_builder: &mut PathBuilder,
114        start_with_move_to: bool,
115    ) -> bool {
116        self.get_segment(start_d, stop_d, path_builder, start_with_move_to)
117    }
118
119    #[must_use]
120    /// Given a start and stop distance, appends the intervening segment(s) to `path_builder`.
121    ///
122    /// If the segment is zero-length, returns `false`; otherwise returns `true`.
123    /// `start_d` and `stop_d` are pinned to legal values (`0..length()`). If
124    /// `start_d > stop_d`, returns `false` and leaves `path_builder` untouched.
125    ///
126    /// Begins the segment with a `move_to` if `start_with_move_to` is `true`.
127    ///
128    /// - `start_d`: start distance along the contour.
129    /// - `stop_d`: stop distance along the contour.
130    /// - `path_builder`: destination that receives the segment.
131    /// - `start_with_move_to`: whether to begin with `move_to`.
132    pub fn get_segment(
133        &self,
134        start_d: scalar,
135        stop_d: scalar,
136        path_builder: &mut PathBuilder,
137        start_with_move_to: bool,
138    ) -> bool {
139        unsafe {
140            self.native().getSegment(
141                start_d,
142                stop_d,
143                path_builder.native_mut(),
144                start_with_move_to,
145            )
146        }
147    }
148
149    /// Returns `true` if the contour is closed.
150    pub fn is_closed(&self) -> bool {
151        unsafe { sb::C_SkContourMeasure_isClosed(self.native()) }
152    }
153
154    /// Returns an iterator over measurement data for the contour's verbs.
155    pub fn verbs(&self) -> ForwardVerbIterator {
156        let iterator =
157            construct(|iterator| unsafe { sb::C_SkContourMeasure_begin(self.native(), iterator) });
158
159        ForwardVerbIterator {
160            iterator,
161            contour_measure: self,
162        }
163    }
164}
165
166/// Utility for iterating over a contour's verbs.
167pub struct ForwardVerbIterator<'a> {
168    iterator: SkContourMeasure_ForwardVerbIterator,
169    contour_measure: &'a ContourMeasure,
170}
171unsafe_send_sync!(ForwardVerbIterator<'_>);
172
173impl fmt::Debug for ForwardVerbIterator<'_> {
174    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
175        f.debug_struct("ForwardVerbIterator").finish()
176    }
177}
178
179impl PartialEq for ForwardVerbIterator<'_> {
180    fn eq(&self, other: &Self) -> bool {
181        unsafe {
182            sb::C_SkContourMeasure_ForwardVerbIterator_Equals(&self.iterator, &other.iterator)
183        }
184    }
185}
186
187impl<'a> Iterator for ForwardVerbIterator<'a> {
188    type Item = VerbMeasure<'a>;
189
190    fn next(&mut self) -> Option<Self::Item> {
191        let end = construct(|end| unsafe {
192            sb::C_SkContourMeasure_end(self.contour_measure.native(), end)
193        });
194        if unsafe { sb::C_SkContourMeasure_ForwardVerbIterator_Equals(&self.iterator, &end) } {
195            return None;
196        }
197        let item = construct(|item| unsafe {
198            sb::C_SkContourMeasure_ForwardVerbIterator_item(&self.iterator, item)
199        });
200        unsafe { sb::C_SkContourMeasure_ForwardVerbIterator_next(&mut self.iterator) };
201        Some(VerbMeasure {
202            verb_measure: item,
203            _pd: PhantomData,
204        })
205    }
206}
207
208/// Measurement data for an individual verb.
209pub struct VerbMeasure<'a> {
210    verb_measure: SkContourMeasure_VerbMeasure,
211    _pd: PhantomData<ForwardVerbIterator<'a>>,
212}
213unsafe_send_sync!(VerbMeasure<'_>);
214
215impl fmt::Debug for VerbMeasure<'_> {
216    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
217        f.debug_struct("VerbMeasure")
218            .field("verb", &self.verb())
219            .field("distance", &self.distance())
220            .field("points", &self.points())
221            .finish()
222    }
223}
224
225impl VerbMeasure<'_> {
226    /// Returns the verb type.
227    pub fn verb(&self) -> PathVerb {
228        self.verb_measure.fVerb
229    }
230
231    /// Returns the cumulative distance along the current contour.
232    pub fn distance(&self) -> scalar {
233        self.verb_measure.fDistance
234    }
235
236    /// Returns the verb points.
237    pub fn points(&self) -> &[Point] {
238        unsafe {
239            safer::from_raw_parts(
240                Point::from_native_ptr(self.verb_measure.fPts.fPtr),
241                self.verb_measure.fPts.fSize,
242            )
243        }
244    }
245}
246
247pub type ContourMeasureIter = Handle<SkContourMeasureIter>;
248unsafe_send_sync!(ContourMeasureIter);
249
250impl NativeDrop for SkContourMeasureIter {
251    fn drop(&mut self) {
252        unsafe {
253            sb::C_SkContourMeasureIter_destruct(self);
254        }
255    }
256}
257
258impl Iterator for ContourMeasureIter {
259    type Item = ContourMeasure;
260
261    /// Iterates through contours in the path, returning a [`ContourMeasure`] for each contour.
262    /// Returns `None` when iteration is complete.
263    ///
264    /// Only non-zero-length contours are returned, where a contour is the segments
265    /// between a move verb and either:
266    /// - the next move verb,
267    /// - one or more close verbs,
268    /// - or the end of the path.
269    ///
270    /// Zero-length contours are skipped.
271    fn next(&mut self) -> Option<Self::Item> {
272        ContourMeasure::from_ptr(unsafe { sb::C_SkContourMeasureIter_next(self.native_mut()) })
273    }
274}
275
276impl fmt::Debug for ContourMeasureIter {
277    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
278        f.debug_struct("ContourMeasureIter").finish()
279    }
280}
281
282impl ContourMeasureIter {
283    /// Initializes the iterator with a path.
284    ///
285    /// The parts of the path that are needed are copied, so the caller is free
286    /// to modify or delete the path after this call.
287    ///
288    /// `res_scale` controls the precision of the measure. Values greater than
289    /// `1` increase precision (and may slow down the computation).
290    ///
291    /// - `path`: source path to iterate.
292    /// - `force_closed`: whether open contours are treated as closed.
293    /// - `res_scale`: optional precision scale (defaults to `1.0`).
294    pub fn new(path: &Path, force_closed: bool, res_scale: impl Into<Option<scalar>>) -> Self {
295        Self::from_path(path, force_closed, res_scale)
296    }
297
298    /// Initializes the iterator with a path.
299    ///
300    /// The parts of the path that are needed are copied, so the caller is free
301    /// to modify or delete the path after this call.
302    ///
303    /// `res_scale` controls the precision of the measure. Values greater than
304    /// `1` increase precision (and may slow down the computation).
305    ///
306    /// - `path`: source path to iterate.
307    /// - `force_closed`: whether open contours are treated as closed.
308    /// - `res_scale`: optional precision scale (defaults to `1.0`).
309    pub fn from_path(
310        path: &Path,
311        force_closed: bool,
312        res_scale: impl Into<Option<scalar>>,
313    ) -> Self {
314        Self::from_native_c(unsafe {
315            SkContourMeasureIter::new1(path.native(), force_closed, res_scale.into().unwrap_or(1.0))
316        })
317    }
318
319    /// Resets the iterator with a path.
320    ///
321    /// The parts of the path that are needed are copied, so the caller is free
322    /// to modify or delete the path after this call.
323    ///
324    /// - `path`: source path to iterate.
325    /// - `force_closed`: whether open contours are treated as closed.
326    /// - `res_scale`: optional precision scale (defaults to `1.0`).
327    pub fn reset(
328        &mut self,
329        path: &Path,
330        force_closed: bool,
331        res_scale: impl Into<Option<scalar>>,
332    ) -> &mut Self {
333        unsafe {
334            self.native_mut()
335                .reset(path.native(), force_closed, res_scale.into().unwrap_or(1.0))
336        }
337        self
338    }
339}
340
341#[cfg(test)]
342mod tests {
343    use super::ContourMeasureIter;
344    use crate::{Path, Rect};
345
346    #[test]
347    fn contour_and_verb_measure() {
348        let p = Path::rect(Rect::new(0.0, 0.0, 10.0, 10.0), None);
349        let measure = ContourMeasureIter::new(&p, true, None);
350        for contour in measure {
351            for verb in contour.verbs() {
352                println!("verb: {verb:?}")
353            }
354        }
355    }
356}