1 | use std::{fmt, ptr, slice}; |
2 | |
3 | use skia_bindings::{ |
4 | self as sb, SkTextBlob, SkTextBlobBuilder, SkTextBlob_Iter, SkTextBlob_Iter_Run, SkTypeface, |
5 | }; |
6 | |
7 | use crate::{ |
8 | prelude::*, scalar, EncodedText, Font, GlyphId, Paint, Point, RSXform, Rect, Typeface, |
9 | }; |
10 | |
11 | pub type TextBlob = RCHandle<SkTextBlob>; |
12 | unsafe_send_sync!(TextBlob); |
13 | require_base_type!(SkTextBlob, sb::SkNVRefCnt); |
14 | |
15 | impl 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 | |
29 | impl fmt::Debug for TextBlob { |
30 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
31 | f&mut DebugStruct<'_, '_>.debug_struct("TextBlob" ) |
32 | .field("bounds" , &self.bounds()) |
33 | .field(name:"unique_id" , &self.unique_id()) |
34 | .finish() |
35 | } |
36 | } |
37 | |
38 | impl 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 | |
135 | pub type TextBlobBuilder = Handle<SkTextBlobBuilder>; |
136 | unsafe_send_sync!(TextBlobBuilder); |
137 | |
138 | impl NativeDrop for SkTextBlobBuilder { |
139 | fn drop(&mut self) { |
140 | unsafe { sb::C_SkTextBlobBuilder_destruct(self) } |
141 | } |
142 | } |
143 | |
144 | impl fmt::Debug for TextBlobBuilder { |
145 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
146 | f.debug_struct(name:"TextBlobBuilder" ).finish() |
147 | } |
148 | } |
149 | |
150 | impl 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 | |
333 | pub type TextBlobIter<'a> = Borrows<'a, Handle<SkTextBlob_Iter>>; |
334 | |
335 | pub struct TextBlobRun<'a> { |
336 | typeface: *mut SkTypeface, |
337 | pub glyph_indices: &'a [u16], |
338 | } |
339 | |
340 | impl fmt::Debug for TextBlobRun<'_> { |
341 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
342 | f&mut DebugStruct<'_, '_>.debug_struct("TextBlobRun" ) |
343 | .field("typeface" , self.typeface()) |
344 | .field(name:"glyph_indices" , &self.glyph_indices) |
345 | .finish() |
346 | } |
347 | } |
348 | |
349 | impl TextBlobRun<'_> { |
350 | pub fn typeface(&self) -> &Option<Typeface> { |
351 | Typeface::from_unshared_ptr_ref(&self.typeface) |
352 | } |
353 | } |
354 | |
355 | impl<'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(_dep:text_blob) |
359 | } |
360 | } |
361 | |
362 | impl NativeDrop for SkTextBlob_Iter { |
363 | fn drop(&mut self) { |
364 | unsafe { sb::C_SkTextBlob_Iter_destruct(self) } |
365 | } |
366 | } |
367 | |
368 | impl<'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 ] |
396 | fn 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 | } |
401 | |