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