skia_safe/core/
text_blob.rs

1use std::{fmt, ptr, slice};
2
3use skia_bindings::{
4    self as sb, SkTextBlob, SkTextBlobBuilder, SkTextBlob_Iter, SkTextBlob_Iter_Run, SkTypeface,
5};
6
7use crate::{
8    prelude::*, scalar, EncodedText, Font, GlyphId, Paint, Point, RSXform, Rect, Typeface,
9};
10
11pub type TextBlob = RCHandle<SkTextBlob>;
12unsafe_send_sync!(TextBlob);
13require_base_type!(SkTextBlob, sb::SkNVRefCnt);
14
15impl NativeRefCounted for SkTextBlob {
16    fn _ref(&self) {
17        unsafe { sb::C_SkTextBlob_ref(self) };
18    }
19
20    fn _unref(&self) {
21        unsafe { sb::C_SkTextBlob_unref(self) }
22    }
23
24    fn unique(&self) -> bool {
25        unsafe { sb::C_SkTextBlob_unique(self) }
26    }
27}
28
29impl fmt::Debug for TextBlob {
30    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31        f.debug_struct("TextBlob")
32            .field("bounds", &self.bounds())
33            .field("unique_id", &self.unique_id())
34            .finish()
35    }
36}
37
38impl TextBlob {
39    pub fn new(str: impl AsRef<str>, font: &Font) -> Option<Self> {
40        Self::from_str(str, font)
41    }
42
43    pub fn bounds(&self) -> &Rect {
44        Rect::from_native_ref(&self.native().fBounds)
45    }
46
47    pub fn unique_id(&self) -> u32 {
48        self.native().fUniqueID
49    }
50
51    // TODO: consider to provide an inplace variant.
52    pub fn get_intercepts(&self, bounds: [scalar; 2], paint: Option<&Paint>) -> Vec<scalar> {
53        unsafe {
54            let count = self.native().getIntercepts(
55                bounds.as_ptr(),
56                ptr::null_mut(),
57                paint.native_ptr_or_null(),
58            );
59            let mut intervals = vec![Default::default(); count.try_into().unwrap()];
60            let count_2 = self.native().getIntercepts(
61                bounds.as_ptr(),
62                intervals.as_mut_ptr(),
63                paint.native_ptr_or_null(),
64            );
65            assert_eq!(count, count_2);
66            intervals
67        }
68    }
69
70    pub fn from_str(str: impl AsRef<str>, font: &Font) -> Option<TextBlob> {
71        Self::from_text(str.as_ref(), font)
72    }
73
74    pub fn from_text(text: impl EncodedText, font: &Font) -> Option<TextBlob> {
75        let (ptr, size, encoding) = text.as_raw();
76        TextBlob::from_ptr(unsafe {
77            sb::C_SkTextBlob_MakeFromText(ptr, size, font.native(), encoding.into_native())
78        })
79    }
80
81    pub fn from_pos_text_h(
82        text: impl EncodedText,
83        x_pos: &[scalar],
84        const_y: scalar,
85        font: &Font,
86    ) -> Option<TextBlob> {
87        // TODO: avoid that somehow.
88        assert_eq!(x_pos.len(), font.count_text(&text));
89        let (ptr, size, encoding) = text.as_raw();
90        TextBlob::from_ptr(unsafe {
91            sb::C_SkTextBlob_MakeFromPosTextH(
92                ptr,
93                size,
94                x_pos.as_ptr(),
95                const_y,
96                font.native(),
97                encoding.into_native(),
98            )
99        })
100    }
101
102    pub fn from_pos_text(text: impl EncodedText, pos: &[Point], font: &Font) -> Option<TextBlob> {
103        assert_eq!(pos.len(), font.count_text(&text));
104        let (ptr, size, encoding) = text.as_raw();
105        TextBlob::from_ptr(unsafe {
106            sb::C_SkTextBlob_MakeFromPosText(
107                ptr,
108                size,
109                pos.native().as_ptr(),
110                font.native(),
111                encoding.into_native(),
112            )
113        })
114    }
115
116    pub fn from_rsxform(
117        text: impl EncodedText,
118        xform: &[RSXform],
119        font: &Font,
120    ) -> Option<TextBlob> {
121        assert_eq!(xform.len(), font.count_text(&text));
122        let (ptr, size, encoding) = text.as_raw();
123        TextBlob::from_ptr(unsafe {
124            sb::C_SkTextBlob_MakeFromRSXform(
125                ptr,
126                size,
127                xform.native().as_ptr(),
128                font.native(),
129                encoding.into_native(),
130            )
131        })
132    }
133}
134
135pub type TextBlobBuilder = Handle<SkTextBlobBuilder>;
136unsafe_send_sync!(TextBlobBuilder);
137
138impl NativeDrop for SkTextBlobBuilder {
139    fn drop(&mut self) {
140        unsafe { sb::C_SkTextBlobBuilder_destruct(self) }
141    }
142}
143
144impl fmt::Debug for TextBlobBuilder {
145    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
146        f.debug_struct("TextBlobBuilder").finish()
147    }
148}
149
150impl TextBlobBuilder {
151    pub fn new() -> Self {
152        Self::from_native_c(unsafe { SkTextBlobBuilder::new() })
153    }
154
155    pub fn make(&mut self) -> Option<TextBlob> {
156        TextBlob::from_ptr(unsafe { sb::C_SkTextBlobBuilder_make(self.native_mut()) })
157    }
158
159    pub fn alloc_run(
160        &mut self,
161        font: &Font,
162        count: usize,
163        offset: impl Into<Point>,
164        bounds: Option<&Rect>,
165    ) -> &mut [GlyphId] {
166        let offset = offset.into();
167        unsafe {
168            let buffer = &*self.native_mut().allocRun(
169                font.native(),
170                count.try_into().unwrap(),
171                offset.x,
172                offset.y,
173                bounds.native_ptr_or_null(),
174            );
175            safer::from_raw_parts_mut(buffer.glyphs, count)
176        }
177    }
178
179    pub fn alloc_run_pos_h(
180        &mut self,
181        font: &Font,
182        count: usize,
183        y: scalar,
184        bounds: Option<&Rect>,
185    ) -> (&mut [GlyphId], &mut [scalar]) {
186        unsafe {
187            let buffer = &*self.native_mut().allocRunPosH(
188                font.native(),
189                count.try_into().unwrap(),
190                y,
191                bounds.native_ptr_or_null(),
192            );
193            (
194                safer::from_raw_parts_mut(buffer.glyphs, count),
195                safer::from_raw_parts_mut(buffer.pos, count),
196            )
197        }
198    }
199
200    pub fn alloc_run_pos(
201        &mut self,
202        font: &Font,
203        count: usize,
204        bounds: Option<&Rect>,
205    ) -> (&mut [GlyphId], &mut [Point]) {
206        unsafe {
207            let buffer = &*self.native_mut().allocRunPos(
208                font.native(),
209                count.try_into().unwrap(),
210                bounds.native_ptr_or_null(),
211            );
212            (
213                safer::from_raw_parts_mut(buffer.glyphs, count),
214                safer::from_raw_parts_mut(buffer.pos as *mut Point, count),
215            )
216        }
217    }
218
219    pub fn alloc_run_rsxform(
220        &mut self,
221        font: &Font,
222        count: usize,
223    ) -> (&mut [GlyphId], &mut [RSXform]) {
224        unsafe {
225            let buffer = &*self
226                .native_mut()
227                .allocRunRSXform(font.native(), count.try_into().unwrap());
228            (
229                safer::from_raw_parts_mut(buffer.glyphs, count),
230                safer::from_raw_parts_mut(buffer.pos as *mut RSXform, count),
231            )
232        }
233    }
234
235    pub fn alloc_run_text(
236        &mut self,
237        font: &Font,
238        count: usize,
239        offset: impl Into<Point>,
240        text_byte_count: usize,
241        bounds: Option<&Rect>,
242    ) -> (&mut [GlyphId], &mut [u8], &mut [u32]) {
243        let offset = offset.into();
244        unsafe {
245            let buffer = &*self.native_mut().allocRunText(
246                font.native(),
247                count.try_into().unwrap(),
248                offset.x,
249                offset.y,
250                text_byte_count.try_into().unwrap(),
251                bounds.native_ptr_or_null(),
252            );
253            (
254                safer::from_raw_parts_mut(buffer.glyphs, count),
255                safer::from_raw_parts_mut(buffer.utf8text as *mut u8, text_byte_count),
256                safer::from_raw_parts_mut(buffer.clusters, count),
257            )
258        }
259    }
260
261    pub fn alloc_run_text_pos_h(
262        &mut self,
263        font: &Font,
264        count: usize,
265        y: scalar,
266        text_byte_count: usize,
267        bounds: Option<&Rect>,
268    ) -> (&mut [GlyphId], &mut [scalar], &mut [u8], &mut [u32]) {
269        unsafe {
270            let buffer = &*self.native_mut().allocRunTextPosH(
271                font.native(),
272                count.try_into().unwrap(),
273                y,
274                text_byte_count.try_into().unwrap(),
275                bounds.native_ptr_or_null(),
276            );
277            (
278                safer::from_raw_parts_mut(buffer.glyphs, count),
279                safer::from_raw_parts_mut(buffer.pos, count),
280                safer::from_raw_parts_mut(buffer.utf8text as *mut u8, text_byte_count),
281                safer::from_raw_parts_mut(buffer.clusters, count),
282            )
283        }
284    }
285
286    pub fn alloc_run_text_pos(
287        &mut self,
288        font: &Font,
289        count: usize,
290        text_byte_count: usize,
291        bounds: Option<&Rect>,
292    ) -> (&mut [GlyphId], &mut [Point], &mut [u8], &mut [u32]) {
293        unsafe {
294            let buffer = &*self.native_mut().allocRunTextPos(
295                font.native(),
296                count.try_into().unwrap(),
297                text_byte_count.try_into().unwrap(),
298                bounds.native_ptr_or_null(),
299            );
300            (
301                safer::from_raw_parts_mut(buffer.glyphs, count),
302                safer::from_raw_parts_mut(buffer.pos as *mut Point, count),
303                safer::from_raw_parts_mut(buffer.utf8text as *mut u8, text_byte_count),
304                safer::from_raw_parts_mut(buffer.clusters, count),
305            )
306        }
307    }
308
309    pub fn alloc_run_text_rsxform(
310        &mut self,
311        font: &Font,
312        count: usize,
313        text_byte_count: usize,
314        bounds: Option<&Rect>,
315    ) -> (&mut [GlyphId], &mut [RSXform], &mut [u8], &mut [u32]) {
316        unsafe {
317            let buffer = &*self.native_mut().allocRunTextPos(
318                font.native(),
319                count.try_into().unwrap(),
320                text_byte_count.try_into().unwrap(),
321                bounds.native_ptr_or_null(),
322            );
323            (
324                safer::from_raw_parts_mut(buffer.glyphs, count),
325                safer::from_raw_parts_mut(buffer.pos as *mut RSXform, count),
326                safer::from_raw_parts_mut(buffer.utf8text as *mut u8, text_byte_count),
327                safer::from_raw_parts_mut(buffer.clusters, count),
328            )
329        }
330    }
331}
332
333pub type TextBlobIter<'a> = Borrows<'a, Handle<SkTextBlob_Iter>>;
334
335pub struct TextBlobRun<'a> {
336    typeface: *mut SkTypeface,
337    pub glyph_indices: &'a [GlyphId],
338}
339
340impl fmt::Debug for TextBlobRun<'_> {
341    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
342        f.debug_struct("TextBlobRun")
343            .field("typeface", self.typeface())
344            .field("glyph_indices", &self.glyph_indices)
345            .finish()
346    }
347}
348
349impl TextBlobRun<'_> {
350    pub fn typeface(&self) -> &Option<Typeface> {
351        Typeface::from_unshared_ptr_ref(&self.typeface)
352    }
353}
354
355impl<'a> Borrows<'a, Handle<SkTextBlob_Iter>> {
356    pub fn new(text_blob: &'a TextBlob) -> Self {
357        Handle::from_native_c(unsafe { SkTextBlob_Iter::new(text_blob.native()) })
358            .borrows(text_blob)
359    }
360}
361
362impl NativeDrop for SkTextBlob_Iter {
363    fn drop(&mut self) {
364        unsafe { sb::C_SkTextBlob_Iter_destruct(self) }
365    }
366}
367
368impl<'a> Iterator for Borrows<'a, Handle<SkTextBlob_Iter>> {
369    type Item = TextBlobRun<'a>;
370    fn next(&mut self) -> Option<Self::Item> {
371        let mut run = SkTextBlob_Iter_Run {
372            fTypeface: ptr::null_mut(),
373            fGlyphCount: 0,
374            fGlyphIndices: ptr::null_mut(),
375        };
376        unsafe {
377            if self.native_mut().next(&mut run) {
378                let indices = if !run.fGlyphIndices.is_null() && run.fGlyphCount != 0 {
379                    slice::from_raw_parts(run.fGlyphIndices, run.fGlyphCount.try_into().unwrap())
380                } else {
381                    &[]
382                };
383
384                Some(TextBlobRun {
385                    typeface: run.fTypeface,
386                    glyph_indices: indices,
387                })
388            } else {
389                None
390            }
391        }
392    }
393}
394
395#[test]
396fn test_point_size_and_alignment_equals_size_of_two_scalars_used_in_alloc_run_pos() {
397    use std::mem;
398    assert_eq!(mem::size_of::<Point>(), mem::size_of::<[scalar; 2]>());
399    assert_eq!(mem::align_of::<Point>(), mem::align_of::<[scalar; 2]>());
400}