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