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 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 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}