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                x_pos.len(),
96                const_y,
97                font.native(),
98                encoding.into_native(),
99            )
100        })
101    }
102
103    pub fn from_pos_text(text: impl EncodedText, pos: &[Point], font: &Font) -> Option<TextBlob> {
104        assert_eq!(pos.len(), font.count_text(&text));
105        let (ptr, size, encoding) = text.as_raw();
106        TextBlob::from_ptr(unsafe {
107            sb::C_SkTextBlob_MakeFromPosText(
108                ptr,
109                size,
110                pos.native().as_ptr(),
111                pos.len(),
112                font.native(),
113                encoding.into_native(),
114            )
115        })
116    }
117
118    pub fn from_rsxform(
119        text: impl EncodedText,
120        xform: &[RSXform],
121        font: &Font,
122    ) -> Option<TextBlob> {
123        assert_eq!(xform.len(), font.count_text(&text));
124        let (ptr, size, encoding) = text.as_raw();
125        TextBlob::from_ptr(unsafe {
126            sb::C_SkTextBlob_MakeFromRSXform(
127                ptr,
128                size,
129                xform.native().as_ptr(),
130                xform.len(),
131                font.native(),
132                encoding.into_native(),
133            )
134        })
135    }
136}
137
138pub type TextBlobBuilder = Handle<SkTextBlobBuilder>;
139unsafe_send_sync!(TextBlobBuilder);
140
141impl NativeDrop for SkTextBlobBuilder {
142    fn drop(&mut self) {
143        unsafe { sb::C_SkTextBlobBuilder_destruct(self) }
144    }
145}
146
147impl fmt::Debug for TextBlobBuilder {
148    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
149        f.debug_struct("TextBlobBuilder").finish()
150    }
151}
152
153impl TextBlobBuilder {
154    pub fn new() -> Self {
155        Self::from_native_c(unsafe { SkTextBlobBuilder::new() })
156    }
157
158    pub fn make(&mut self) -> Option<TextBlob> {
159        TextBlob::from_ptr(unsafe { sb::C_SkTextBlobBuilder_make(self.native_mut()) })
160    }
161
162    pub fn alloc_run(
163        &mut self,
164        font: &Font,
165        count: usize,
166        offset: impl Into<Point>,
167        bounds: Option<&Rect>,
168    ) -> &mut [GlyphId] {
169        let offset = offset.into();
170        unsafe {
171            let buffer = &*self.native_mut().allocRun(
172                font.native(),
173                count.try_into().unwrap(),
174                offset.x,
175                offset.y,
176                bounds.native_ptr_or_null(),
177            );
178            safer::from_raw_parts_mut(buffer.glyphs, count)
179        }
180    }
181
182    pub fn alloc_run_pos_h(
183        &mut self,
184        font: &Font,
185        count: usize,
186        y: scalar,
187        bounds: Option<&Rect>,
188    ) -> (&mut [GlyphId], &mut [scalar]) {
189        unsafe {
190            let buffer = &*self.native_mut().allocRunPosH(
191                font.native(),
192                count.try_into().unwrap(),
193                y,
194                bounds.native_ptr_or_null(),
195            );
196            (
197                safer::from_raw_parts_mut(buffer.glyphs, count),
198                safer::from_raw_parts_mut(buffer.pos, count),
199            )
200        }
201    }
202
203    pub fn alloc_run_pos(
204        &mut self,
205        font: &Font,
206        count: usize,
207        bounds: Option<&Rect>,
208    ) -> (&mut [GlyphId], &mut [Point]) {
209        unsafe {
210            let buffer = &*self.native_mut().allocRunPos(
211                font.native(),
212                count.try_into().unwrap(),
213                bounds.native_ptr_or_null(),
214            );
215            (
216                safer::from_raw_parts_mut(buffer.glyphs, count),
217                safer::from_raw_parts_mut(buffer.pos as *mut Point, count),
218            )
219        }
220    }
221
222    pub fn alloc_run_rsxform(
223        &mut self,
224        font: &Font,
225        count: usize,
226    ) -> (&mut [GlyphId], &mut [RSXform]) {
227        unsafe {
228            let buffer = &*self
229                .native_mut()
230                .allocRunRSXform(font.native(), count.try_into().unwrap());
231            (
232                safer::from_raw_parts_mut(buffer.glyphs, count),
233                safer::from_raw_parts_mut(buffer.pos as *mut RSXform, count),
234            )
235        }
236    }
237
238    pub fn alloc_run_text(
239        &mut self,
240        font: &Font,
241        count: usize,
242        offset: impl Into<Point>,
243        text_byte_count: usize,
244        bounds: Option<&Rect>,
245    ) -> (&mut [GlyphId], &mut [u8], &mut [u32]) {
246        let offset = offset.into();
247        unsafe {
248            let buffer = &*self.native_mut().allocRunText(
249                font.native(),
250                count.try_into().unwrap(),
251                offset.x,
252                offset.y,
253                text_byte_count.try_into().unwrap(),
254                bounds.native_ptr_or_null(),
255            );
256            (
257                safer::from_raw_parts_mut(buffer.glyphs, count),
258                safer::from_raw_parts_mut(buffer.utf8text as *mut u8, text_byte_count),
259                safer::from_raw_parts_mut(buffer.clusters, count),
260            )
261        }
262    }
263
264    pub fn alloc_run_text_pos_h(
265        &mut self,
266        font: &Font,
267        count: usize,
268        y: scalar,
269        text_byte_count: usize,
270        bounds: Option<&Rect>,
271    ) -> (&mut [GlyphId], &mut [scalar], &mut [u8], &mut [u32]) {
272        unsafe {
273            let buffer = &*self.native_mut().allocRunTextPosH(
274                font.native(),
275                count.try_into().unwrap(),
276                y,
277                text_byte_count.try_into().unwrap(),
278                bounds.native_ptr_or_null(),
279            );
280            (
281                safer::from_raw_parts_mut(buffer.glyphs, count),
282                safer::from_raw_parts_mut(buffer.pos, count),
283                safer::from_raw_parts_mut(buffer.utf8text as *mut u8, text_byte_count),
284                safer::from_raw_parts_mut(buffer.clusters, count),
285            )
286        }
287    }
288
289    pub fn alloc_run_text_pos(
290        &mut self,
291        font: &Font,
292        count: usize,
293        text_byte_count: usize,
294        bounds: Option<&Rect>,
295    ) -> (&mut [GlyphId], &mut [Point], &mut [u8], &mut [u32]) {
296        unsafe {
297            let buffer = &*self.native_mut().allocRunTextPos(
298                font.native(),
299                count.try_into().unwrap(),
300                text_byte_count.try_into().unwrap(),
301                bounds.native_ptr_or_null(),
302            );
303            (
304                safer::from_raw_parts_mut(buffer.glyphs, count),
305                safer::from_raw_parts_mut(buffer.pos as *mut Point, count),
306                safer::from_raw_parts_mut(buffer.utf8text as *mut u8, text_byte_count),
307                safer::from_raw_parts_mut(buffer.clusters, count),
308            )
309        }
310    }
311
312    pub fn alloc_run_text_rsxform(
313        &mut self,
314        font: &Font,
315        count: usize,
316        text_byte_count: usize,
317        bounds: Option<&Rect>,
318    ) -> (&mut [GlyphId], &mut [RSXform], &mut [u8], &mut [u32]) {
319        unsafe {
320            let buffer = &*self.native_mut().allocRunTextPos(
321                font.native(),
322                count.try_into().unwrap(),
323                text_byte_count.try_into().unwrap(),
324                bounds.native_ptr_or_null(),
325            );
326            (
327                safer::from_raw_parts_mut(buffer.glyphs, count),
328                safer::from_raw_parts_mut(buffer.pos as *mut RSXform, count),
329                safer::from_raw_parts_mut(buffer.utf8text as *mut u8, text_byte_count),
330                safer::from_raw_parts_mut(buffer.clusters, count),
331            )
332        }
333    }
334}
335
336pub type TextBlobIter<'a> = Borrows<'a, Handle<SkTextBlob_Iter>>;
337
338pub struct TextBlobRun<'a> {
339    typeface: *mut SkTypeface,
340    pub glyph_indices: &'a [GlyphId],
341}
342
343impl fmt::Debug for TextBlobRun<'_> {
344    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
345        f.debug_struct("TextBlobRun")
346            .field("typeface", self.typeface())
347            .field("glyph_indices", &self.glyph_indices)
348            .finish()
349    }
350}
351
352impl TextBlobRun<'_> {
353    pub fn typeface(&self) -> &Option<Typeface> {
354        Typeface::from_unshared_ptr_ref(&self.typeface)
355    }
356}
357
358impl<'a> Borrows<'a, Handle<SkTextBlob_Iter>> {
359    pub fn new(text_blob: &'a TextBlob) -> Self {
360        Handle::from_native_c(unsafe { SkTextBlob_Iter::new(text_blob.native()) })
361            .borrows(text_blob)
362    }
363}
364
365impl NativeDrop for SkTextBlob_Iter {
366    fn drop(&mut self) {
367        unsafe { sb::C_SkTextBlob_Iter_destruct(self) }
368    }
369}
370
371impl<'a> Iterator for Borrows<'a, Handle<SkTextBlob_Iter>> {
372    type Item = TextBlobRun<'a>;
373    fn next(&mut self) -> Option<Self::Item> {
374        let mut run = SkTextBlob_Iter_Run {
375            fTypeface: ptr::null_mut(),
376            fGlyphCount: 0,
377            fGlyphIndices: ptr::null_mut(),
378        };
379        unsafe {
380            if self.native_mut().next(&mut run) {
381                let indices = if !run.fGlyphIndices.is_null() && run.fGlyphCount != 0 {
382                    slice::from_raw_parts(run.fGlyphIndices, run.fGlyphCount.try_into().unwrap())
383                } else {
384                    &[]
385                };
386
387                Some(TextBlobRun {
388                    typeface: run.fTypeface,
389                    glyph_indices: indices,
390                })
391            } else {
392                None
393            }
394        }
395    }
396}
397
398#[test]
399fn test_point_size_and_alignment_equals_size_of_two_scalars_used_in_alloc_run_pos() {
400    use std::mem;
401    assert_eq!(mem::size_of::<Point>(), mem::size_of::<[scalar; 2]>());
402    assert_eq!(mem::align_of::<Point>(), mem::align_of::<[scalar; 2]>());
403}