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