1use std::cell::RefCell;
2use std::ffi::OsStr;
3use std::fs;
4use std::hash::{
5 Hash,
6 Hasher,
7};
8use std::ops::Range;
9use std::path::Path as FilePath;
10use std::rc::Rc;
11
12use fnv::{
13 FnvBuildHasher,
14 FnvHashMap,
15 FnvHasher,
16};
17use generational_arena::{
18 Arena,
19 Index,
20};
21use lru::LruCache;
22
23use unicode_bidi::BidiInfo;
24use unicode_segmentation::UnicodeSegmentation;
25
26use crate::{
27 Canvas,
28 Color,
29 ErrorKind,
30 FillRule,
31 ImageFlags,
32 ImageId,
33 ImageInfo,
34 Paint,
35 PixelFormat,
36 RenderTarget,
37 Renderer,
38};
39
40mod atlas;
41pub use atlas::Atlas;
42
43mod font;
44use font::Font;
45pub use font::FontMetrics;
46
47use self::font::GlyphRendering;
48
49const GLYPH_PADDING: u32 = 1;
52const GLYPH_MARGIN: u32 = 1;
57
58const TEXTURE_SIZE: usize = 512;
59const LRU_CACHE_CAPACITY: usize = 1000;
60
61#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
63pub struct FontId(Index);
64
65#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
67#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
68pub enum Baseline {
69 Top,
71 Middle,
73 Alphabetic,
75 Bottom,
77}
78
79impl Default for Baseline {
80 fn default() -> Self {
81 Self::Alphabetic
82 }
83}
84
85#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
87#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
88pub enum Align {
89 Left,
91 Center,
93 Right,
95}
96
97impl Default for Align {
98 fn default() -> Self {
99 Self::Left
100 }
101}
102
103#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
104pub enum RenderMode {
105 Fill,
106 Stroke,
107}
108
109impl Default for RenderMode {
110 fn default() -> Self {
111 Self::Fill
112 }
113}
114
115#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
116pub(crate) struct RenderedGlyphId {
117 glyph_index: u32,
118 font_id: FontId,
119 size: u32,
120 line_width: u32,
121 render_mode: RenderMode,
122 subpixel_location: u8,
123}
124
125impl RenderedGlyphId {
126 fn new(glyph_index: u32, font_id: FontId, paint: &Paint, mode: RenderMode, subpixel_location: u8) -> Self {
127 Self {
128 glyph_index,
129 font_id,
130 size: (paint.font_size * 10.0).trunc() as u32,
131 line_width: (paint.line_width * 10.0).trunc() as u32,
132 render_mode: mode,
133 subpixel_location,
134 }
135 }
136}
137
138#[allow(dead_code)]
139#[derive(Copy, Clone, Debug)]
140pub(crate) struct RenderedGlyph {
141 texture_index: usize,
142 width: u32,
143 height: u32,
144 bearing_y: i32,
145 atlas_x: u32,
146 atlas_y: u32,
147 padding: u32,
148 color_glyph: bool,
149}
150
151#[derive(Copy, Clone, Debug)]
152pub struct ShapedGlyph {
153 pub x: f32,
154 pub y: f32,
155 pub c: char,
156 pub byte_index: usize,
157 pub font_id: FontId,
158 pub codepoint: u32,
159 pub width: f32,
160 pub height: f32,
161 pub advance_x: f32,
162 pub advance_y: f32,
163 pub offset_x: f32,
164 pub offset_y: f32,
165 pub bearing_x: f32,
166 pub bearing_y: f32,
167 pub bitmap_glyph: bool,
168}
169
170#[derive(Clone, Debug, Default)]
171struct ShapedWord {
172 glyphs: Vec<ShapedGlyph>,
173 width: f32,
174}
175
176#[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
177struct ShapingId {
178 size: u32,
179 word_hash: u64,
180 font_ids: [Option<FontId>; 8],
181}
182
183impl ShapingId {
184 fn new(paint: &Paint, word: &str, max_width: Option<f32>) -> Self {
185 let mut hasher = FnvHasher::default();
186 word.hash(&mut hasher);
187 if let Some(max_width) = max_width {
188 (max_width.trunc() as i32).hash(&mut hasher);
189 }
190
191 Self {
192 size: (paint.font_size * 10.0).trunc() as u32,
193 word_hash: hasher.finish(),
194 font_ids: paint.font_ids,
195 }
196 }
197}
198
199type ShapedWordsCache<H> = LruCache<ShapingId, Result<ShapedWord, ErrorKind>, H>;
200type ShapingRunCache<H> = LruCache<ShapingId, TextMetrics, H>;
201
202pub(crate) struct FontTexture {
203 atlas: Atlas,
204 pub(crate) image_id: ImageId,
205}
206
207#[derive(Default, Clone)]
224pub struct TextContext(pub(crate) Rc<RefCell<TextContextImpl>>);
225
226impl TextContext {
227 pub fn add_font_dir<T: AsRef<FilePath>>(&self, path: T) -> Result<Vec<FontId>, ErrorKind> {
230 self.0.as_ref().borrow_mut().add_font_dir(path)
231 }
232
233 pub fn add_font_file<T: AsRef<FilePath>>(&self, path: T) -> Result<FontId, ErrorKind> {
236 self.0.as_ref().borrow_mut().add_font_file(path)
237 }
238
239 pub fn add_font_mem(&self, data: &[u8]) -> Result<FontId, ErrorKind> {
242 self.0.as_ref().borrow_mut().add_font_mem(data)
243 }
244
245 pub fn add_shared_font_with_index<T: AsRef<[u8]> + 'static>(
250 &self,
251 data: T,
252 face_index: u32,
253 ) -> Result<FontId, ErrorKind> {
254 self.0
255 .as_ref()
256 .borrow_mut()
257 .add_shared_font_with_index(data, face_index)
258 }
259
260 pub fn measure_text<S: AsRef<str>>(&self, x: f32, y: f32, text: S, paint: Paint) -> Result<TextMetrics, ErrorKind> {
262 self.0.as_ref().borrow_mut().measure_text(x, y, text, paint)
263 }
264
265 pub fn break_text<S: AsRef<str>>(&self, max_width: f32, text: S, paint: Paint) -> Result<usize, ErrorKind> {
269 self.0.as_ref().borrow_mut().break_text(max_width, text, paint)
270 }
271
272 pub fn break_text_vec<S: AsRef<str>>(
274 &self,
275 max_width: f32,
276 text: S,
277 paint: Paint,
278 ) -> Result<Vec<Range<usize>>, ErrorKind> {
279 self.0.as_ref().borrow_mut().break_text_vec(max_width, text, paint)
280 }
281
282 pub fn measure_font(&self, paint: Paint) -> Result<FontMetrics, ErrorKind> {
284 self.0.as_ref().borrow_mut().measure_font(paint)
285 }
286}
287
288pub(crate) struct TextContextImpl {
289 fonts: Arena<Font>,
290 shaping_run_cache: ShapingRunCache<FnvBuildHasher>,
291 shaped_words_cache: ShapedWordsCache<FnvBuildHasher>,
292}
293
294impl Default for TextContextImpl {
295 fn default() -> Self {
296 let fnv_run = FnvBuildHasher::default();
297 let fnv_words = FnvBuildHasher::default();
298
299 Self {
300 fonts: Default::default(),
301 shaping_run_cache: LruCache::with_hasher(LRU_CACHE_CAPACITY, fnv_run),
302 shaped_words_cache: LruCache::with_hasher(LRU_CACHE_CAPACITY, fnv_words),
303 }
304 }
305}
306
307impl TextContextImpl {
308 pub fn add_font_dir<T: AsRef<FilePath>>(&mut self, path: T) -> Result<Vec<FontId>, ErrorKind> {
309 let path = path.as_ref();
310 let mut fonts = Vec::new();
311
312 if path.is_dir() {
313 for entry in fs::read_dir(path)? {
314 let entry = entry?;
315 let path = entry.path();
316
317 if path.is_dir() {
318 self.add_font_dir(&path)?;
319 } else if let Some("ttf") = path.extension().and_then(OsStr::to_str) {
320 fonts.push(self.add_font_file(path)?);
321 } else if let Some("ttc") = path.extension().and_then(OsStr::to_str) {
322 fonts.extend(self.add_font_file_collection(path)?);
323 }
324 }
325 }
326
327 Ok(fonts)
328 }
329
330 pub fn add_font_file<T: AsRef<FilePath>>(&mut self, path: T) -> Result<FontId, ErrorKind> {
331 let data = std::fs::read(path)?;
332
333 self.add_font_mem(&data)
334 }
335
336 pub fn add_font_file_collection<T: AsRef<FilePath>>(
337 &mut self,
338 path: T,
339 ) -> Result<impl Iterator<Item = FontId> + '_, ErrorKind> {
340 let data = std::fs::read(path)?;
341
342 let count = ttf_parser::fonts_in_collection(&data).unwrap_or(1);
343 Ok((0..count).filter_map(move |index| self.add_font_mem_with_index(&data, index).ok()))
344 }
345
346 pub fn add_font_mem(&mut self, data: &[u8]) -> Result<FontId, ErrorKind> {
347 self.add_font_mem_with_index(data, 0)
348 }
349
350 pub fn add_font_mem_with_index(&mut self, data: &[u8], face_index: u32) -> Result<FontId, ErrorKind> {
351 self.clear_caches();
352
353 let font = Font::new_with_data(data.to_owned(), face_index)?;
354 Ok(FontId(self.fonts.insert(font)))
355 }
356
357 pub fn add_shared_font_with_index<T: AsRef<[u8]> + 'static>(
358 &mut self,
359 data: T,
360 face_index: u32,
361 ) -> Result<FontId, ErrorKind> {
362 self.clear_caches();
363
364 let font = Font::new_with_data(data, face_index)?;
365 Ok(FontId(self.fonts.insert(font)))
366 }
367
368 pub fn font(&self, id: FontId) -> Option<&Font> {
369 self.fonts.get(id.0)
370 }
371
372 pub fn font_mut(&mut self, id: FontId) -> Option<&mut Font> {
373 self.fonts.get_mut(id.0)
374 }
375
376 pub fn find_font<F, T>(&mut self, paint: &Paint, mut callback: F) -> Result<T, ErrorKind>
377 where
378 F: FnMut((FontId, &mut Font)) -> (bool, T),
379 {
380 for maybe_font_id in paint.font_ids.iter() {
382 if let Some(font_id) = maybe_font_id {
383 if let Some(font) = self.fonts.get_mut(font_id.0) {
384 let (has_missing, result) = callback((*font_id, font));
385
386 if !has_missing {
387 return Ok(result);
388 }
389 }
390 } else {
391 break;
392 }
393 }
394
395 for (id, font) in &mut self.fonts {
398 let (has_missing, result) = callback((FontId(id), font));
399
400 if !has_missing {
401 return Ok(result);
402 }
403 }
404
405 if let Some((id, font)) = self.fonts.iter_mut().next() {
407 return Ok(callback((FontId(id), font)).1);
408 }
409
410 Err(ErrorKind::NoFontFound)
411 }
412
413 fn clear_caches(&mut self) {
414 self.shaped_words_cache.clear();
415 }
416
417 pub fn measure_text<S: AsRef<str>>(
418 &mut self,
419 x: f32,
420 y: f32,
421 text: S,
422 paint: Paint,
423 ) -> Result<TextMetrics, ErrorKind> {
424 shape(x, y, self, &paint, text.as_ref(), None)
425 }
426
427 pub fn break_text<S: AsRef<str>>(&mut self, max_width: f32, text: S, paint: Paint) -> Result<usize, ErrorKind> {
428 let layout = shape(0.0, 0.0, self, &paint, text.as_ref(), Some(max_width))?;
429
430 Ok(layout.final_byte_index)
431 }
432
433 pub fn break_text_vec<S: AsRef<str>>(
434 &mut self,
435 max_width: f32,
436 text: S,
437 paint: Paint,
438 ) -> Result<Vec<Range<usize>>, ErrorKind> {
439 let text = text.as_ref();
440
441 let mut res = Vec::new();
442 let mut start = 0;
443
444 while start < text.len() {
445 if let Ok(index) = self.break_text(max_width, &text[start..], paint) {
446 if index == 0 {
447 break;
448 }
449
450 let index = start + index;
451 res.push(start..index);
452 start += &text[start..index].len();
453 } else {
454 break;
455 }
456 }
457
458 Ok(res)
459 }
460
461 pub fn measure_font(&mut self, paint: Paint) -> Result<FontMetrics, ErrorKind> {
462 if let Some(Some(id)) = paint.font_ids.get(0) {
463 if let Some(font) = self.font(*id) {
464 return Ok(font.metrics(paint.font_size));
465 }
466 }
467
468 Err(ErrorKind::NoFontFound)
469 }
470}
471
472#[derive(Clone, Default, Debug)]
474pub struct TextMetrics {
475 pub x: f32,
477 pub y: f32,
479 width: f32,
480 height: f32,
481 pub glyphs: Vec<ShapedGlyph>,
483 pub(crate) final_byte_index: usize,
484}
485
486impl TextMetrics {
487 pub(crate) fn scale(&mut self, scale: f32) {
488 self.x *= scale;
489 self.y *= scale;
490 self.width *= scale;
491 self.height *= scale;
492
493 for glyph in &mut self.glyphs {
494 glyph.x *= scale;
495 glyph.y *= scale;
496 glyph.width *= scale;
497 glyph.height *= scale;
498 }
499 }
500
501 pub fn width(&self) -> f32 {
503 self.width
504 }
505
506 pub fn height(&self) -> f32 {
508 self.height
509 }
510
511 pub(crate) fn has_bitmap_glyphs(&self) -> bool {
512 self.glyphs.iter().find(|g| g.bitmap_glyph).is_some()
513 }
514}
515
516pub(crate) fn shape(
519 x: f32,
520 y: f32,
521 context: &mut TextContextImpl,
522 paint: &Paint,
523 text: &str,
524 max_width: Option<f32>,
525) -> Result<TextMetrics, ErrorKind> {
526 let id = ShapingId::new(paint, text, max_width);
527
528 if !context.shaping_run_cache.contains(&id) {
529 let metrics = shape_run(context, paint, text, max_width)?;
530 context.shaping_run_cache.put(id, metrics);
531 }
532
533 if let Some(mut metrics) = context.shaping_run_cache.get(&id).cloned() {
534 layout(x, y, context, &mut metrics, paint)?;
535
536 return Ok(metrics);
537 }
538
539 Err(ErrorKind::UnknownError)
540}
541
542fn shape_run(
543 context: &mut TextContextImpl,
544 paint: &Paint,
545 text: &str,
546 max_width: Option<f32>,
547) -> Result<TextMetrics, ErrorKind> {
548 let mut result = TextMetrics {
549 x: 0.0,
550 y: 0.0,
551 width: 0.0,
552 height: 0.0,
553 glyphs: Vec::with_capacity(text.len()),
554 final_byte_index: 0,
555 };
556
557 let bidi_info = BidiInfo::new(text, Some(unicode_bidi::Level::ltr()));
558
559 if let Some(paragraph) = bidi_info.paragraphs.get(0) {
560 let line = paragraph.range.clone();
561
562 let (levels, runs) = bidi_info.visual_runs(paragraph, line);
563
564 for run in runs.iter() {
565 let sub_text = &text[run.clone()];
566
567 if sub_text.is_empty() {
568 continue;
569 }
570
571 let hb_direction = if levels[run.start].is_rtl() {
572 rustybuzz::Direction::RightToLeft
573 } else {
574 rustybuzz::Direction::LeftToRight
575 };
576
577 let mut words = Vec::new();
578 let mut word_break_reached = false;
579 let mut byte_index = run.start;
580
581 for word in sub_text.split_word_bounds() {
582 let id = ShapingId::new(paint, word, max_width);
583
584 if !context.shaped_words_cache.contains(&id) {
585 let word = shape_word(word, hb_direction, context, paint);
586 context.shaped_words_cache.put(id, word);
587 }
588
589 if let Some(Ok(word)) = context.shaped_words_cache.get(&id) {
590 let mut word = word.clone();
591
592 if let Some(max_width) = max_width {
593 if result.width + word.width >= max_width {
594 word_break_reached = true;
595 break;
596 }
597 }
598
599 result.width += word.width;
600
601 for glyph in &mut word.glyphs {
602 glyph.byte_index += byte_index;
603 debug_assert!(text.get(glyph.byte_index..).is_some());
604 }
605
606 words.push(word);
607 }
608
609 byte_index += word.len();
610 }
611
612 if levels[run.start].is_rtl() {
613 words.reverse();
614 }
615
616 for word in words {
617 result.glyphs.extend(word.glyphs.clone());
618 }
619
620 result.final_byte_index = byte_index;
621
622 if word_break_reached {
623 break;
624 }
625 }
626 }
627
628 Ok(result)
629}
630
631fn shape_word(
632 word: &str,
633 hb_direction: rustybuzz::Direction,
634 context: &mut TextContextImpl,
635 paint: &Paint,
636) -> Result<ShapedWord, ErrorKind> {
637 context.find_font(paint, |(font_id, font)| {
640 let output = {
642 let face = font.face_ref();
643
644 let mut buffer = rustybuzz::UnicodeBuffer::new();
645 buffer.push_str(word);
646 buffer.set_direction(hb_direction);
647
648 rustybuzz::shape(face, &[], buffer)
649 };
650
651 let positions = output.glyph_positions();
652 let infos = output.glyph_infos();
653
654 let mut shaped_word = ShapedWord {
655 glyphs: Vec::with_capacity(positions.len()),
656 width: 0.0,
657 };
658
659 let mut has_missing = false;
660
661 for (position, (info, c)) in positions.iter().zip(infos.iter().zip(word.chars())) {
662 if info.glyph_id == 0 {
663 has_missing = true;
664 }
665
666 let scale = font.scale(paint.font_size);
667
668 let mut g = ShapedGlyph {
669 x: 0.0,
670 y: 0.0,
671 c,
672 byte_index: info.cluster as usize,
673 font_id,
674 codepoint: info.glyph_id,
675 width: 0.0,
676 height: 0.0,
677 advance_x: position.x_advance as f32 * scale,
678 advance_y: position.y_advance as f32 * scale,
679 offset_x: position.x_offset as f32 * scale,
680 offset_y: position.y_offset as f32 * scale,
681 bearing_x: 0.0,
682 bearing_y: 0.0,
683 bitmap_glyph: false,
684 };
685
686 if let Some(glyph) = font.glyph(info.glyph_id as u16) {
687 g.width = glyph.metrics.width * scale;
688 g.height = glyph.metrics.height * scale;
689 g.bearing_x = glyph.metrics.bearing_x * scale;
690 g.bearing_y = glyph.metrics.bearing_y * scale;
691 g.bitmap_glyph = glyph.path.is_none();
692 }
693
694 shaped_word.width += g.advance_x + paint.letter_spacing;
695 shaped_word.glyphs.push(g);
696 }
697
698 (has_missing, shaped_word)
699 })
700}
701
702fn layout(
704 x: f32,
705 y: f32,
706 context: &mut TextContextImpl,
707 res: &mut TextMetrics,
708 paint: &Paint,
709) -> Result<(), ErrorKind> {
710 let mut cursor_x = x;
711 let mut cursor_y = y;
712
713 match paint.text_align {
715 Align::Center => cursor_x -= res.width / 2.0,
716 Align::Right => cursor_x -= res.width,
717 _ => (),
718 }
719
720 res.x = cursor_x;
721
722 let mut min_y = cursor_y;
723 let mut max_y = cursor_y;
724
725 let mut ascender: f32 = 0.;
726 let mut descender: f32 = 0.;
727
728 for glyph in &mut res.glyphs {
729 let font = context.font_mut(glyph.font_id).ok_or(ErrorKind::NoFontFound)?;
730 let metrics = font.metrics(paint.font_size);
731 ascender = ascender.max(metrics.ascender());
732 descender = descender.min(metrics.descender());
733 }
734
735 let primary_metrics = context.find_font(paint, |(_, font)| (false, font.metrics(paint.font_size)))?;
736 if ascender.abs() < std::f32::EPSILON {
737 ascender = primary_metrics.ascender();
738 }
739 if descender.abs() < std::f32::EPSILON {
740 descender = primary_metrics.descender();
741 }
742
743 let alignment_offset_y = match paint.text_baseline {
745 Baseline::Top => ascender,
746 Baseline::Middle => (ascender + descender) / 2.0,
747 Baseline::Alphabetic => 0.0,
748 Baseline::Bottom => descender,
749 };
750
751 for glyph in &mut res.glyphs {
752 glyph.x = cursor_x + glyph.offset_x + glyph.bearing_x;
753 glyph.y = (cursor_y + alignment_offset_y).round() + glyph.offset_y - glyph.bearing_y;
754
755 min_y = min_y.min(glyph.y);
756 max_y = max_y.max(glyph.y + glyph.height);
757
758 cursor_x += glyph.advance_x + paint.letter_spacing;
759 cursor_y += glyph.advance_y;
760 }
761
762 res.y = min_y;
763 res.height = max_y - min_y;
764
765 Ok(())
766}
767
768#[derive(Clone, Debug)]
771pub(crate) struct DrawCmd {
772 pub image_id: ImageId,
773 pub quads: Vec<Quad>,
774}
775
776#[derive(Copy, Clone, Default, Debug)]
777pub(crate) struct Quad {
778 pub x0: f32,
779 pub y0: f32,
780 pub s0: f32,
781 pub t0: f32,
782 pub x1: f32,
783 pub y1: f32,
784 pub s1: f32,
785 pub t1: f32,
786}
787
788pub(crate) struct GlyphDrawCommands {
789 pub(crate) alpha_glyphs: Vec<DrawCmd>,
790 pub(crate) color_glyphs: Vec<DrawCmd>,
791}
792
793#[derive(Default)]
794pub(crate) struct GlyphAtlas {
795 pub rendered_glyphs: RefCell<FnvHashMap<RenderedGlyphId, RenderedGlyph>>,
796 pub glyph_textures: RefCell<Vec<FontTexture>>,
797}
798
799impl GlyphAtlas {
800 pub(crate) fn render_atlas<T: Renderer>(
801 &self,
802 canvas: &mut Canvas<T>,
803 text_layout: &TextMetrics,
804 paint: &Paint,
805 mode: RenderMode,
806 ) -> Result<GlyphDrawCommands, ErrorKind> {
807 let mut alpha_cmd_map = FnvHashMap::default();
808 let mut color_cmd_map = FnvHashMap::default();
809
810 let line_width_offset = if mode == RenderMode::Stroke {
811 (paint.line_width / 2.0).ceil()
812 } else {
813 0.0
814 };
815
816 let initial_render_target = canvas.current_render_target;
817
818 for glyph in &text_layout.glyphs {
819 let subpixel_location = crate::geometry::quantize(glyph.x.fract(), 0.1) * 10.0;
820
821 let id = RenderedGlyphId::new(glyph.codepoint, glyph.font_id, paint, mode, subpixel_location as u8);
822
823 if !self.rendered_glyphs.borrow().contains_key(&id) {
824 let glyph = self.render_glyph(canvas, paint, mode, glyph)?;
825
826 self.rendered_glyphs.borrow_mut().insert(id, glyph);
827 }
828
829 let rendered_glyphs = self.rendered_glyphs.borrow();
830 let rendered = rendered_glyphs.get(&id).unwrap();
831
832 if let Some(texture) = self.glyph_textures.borrow().get(rendered.texture_index) {
833 let image_id = texture.image_id;
834 let size = texture.atlas.size();
835 let itw = 1.0 / size.0 as f32;
836 let ith = 1.0 / size.1 as f32;
837
838 let cmd_map = if rendered.color_glyph {
839 &mut color_cmd_map
840 } else {
841 &mut alpha_cmd_map
842 };
843
844 let cmd = cmd_map.entry(rendered.texture_index).or_insert_with(|| DrawCmd {
845 image_id,
846 quads: Vec::new(),
847 });
848
849 let mut q = Quad::default();
850
851 let line_width_offset = if rendered.color_glyph { 0. } else { line_width_offset };
852
853 q.x0 = glyph.x.trunc() - line_width_offset - GLYPH_PADDING as f32;
854 q.y0 = (glyph.y + glyph.bearing_y).round()
855 - rendered.bearing_y as f32
856 - line_width_offset
857 - GLYPH_PADDING as f32;
858 q.x1 = q.x0 + rendered.width as f32;
859 q.y1 = q.y0 + rendered.height as f32;
860
861 q.s0 = rendered.atlas_x as f32 * itw;
862 q.t0 = rendered.atlas_y as f32 * ith;
863 q.s1 = (rendered.atlas_x + rendered.width) as f32 * itw;
864 q.t1 = (rendered.atlas_y + rendered.height) as f32 * ith;
865
866 cmd.quads.push(q);
867 }
868 }
869
870 canvas.set_render_target(initial_render_target);
871
872 Ok(GlyphDrawCommands {
873 alpha_glyphs: alpha_cmd_map.drain().map(|(_, cmd)| cmd).collect(),
874 color_glyphs: color_cmd_map.drain().map(|(_, cmd)| cmd).collect(),
875 })
876 }
877
878 fn render_glyph<T: Renderer>(
879 &self,
880 canvas: &mut Canvas<T>,
881 paint: &Paint,
882 mode: RenderMode,
883 glyph: &ShapedGlyph,
884 ) -> Result<RenderedGlyph, ErrorKind> {
885 let padding = GLYPH_PADDING + GLYPH_MARGIN;
886
887 let text_context = canvas.text_context.clone();
888 let mut text_context = text_context.borrow_mut();
889
890 let (mut maybe_glyph_representation, scale) = {
891 let font = text_context.font_mut(glyph.font_id).ok_or(ErrorKind::NoFontFound)?;
892 let scale = font.scale(paint.font_size);
893
894 let maybe_glyph_representation =
895 font.glyph_rendering_representation(glyph.codepoint as u16, paint.font_size as u16);
896 (maybe_glyph_representation, scale)
897 };
898
899 #[cfg(feature = "image-loading")]
900 let color_glyph = matches!(maybe_glyph_representation, Some(GlyphRendering::RenderAsImage(..)));
901 #[cfg(not(feature = "image-loading"))]
902 let color_glyph = false;
903
904 let line_width = if color_glyph || mode != RenderMode::Stroke {
905 0.0
906 } else {
907 paint.line_width
908 };
909
910 let line_width_offset = (line_width / 2.0).ceil();
911
912 let width = glyph.width.ceil() as u32 + (line_width_offset * 2.0) as u32 + padding * 2;
913 let height = glyph.height.ceil() as u32 + (line_width_offset * 2.0) as u32 + padding * 2;
914
915 let (dst_index, dst_image_id, (dst_x, dst_y)) =
916 self.find_texture_or_alloc(canvas, width as usize, height as usize)?;
917
918 canvas.save();
920 canvas.reset();
921
922 let rendered_bearing_y = glyph.bearing_y.round();
923 let x_quant = crate::geometry::quantize(glyph.x.fract(), 0.1);
924 let x = dst_x as f32 - glyph.bearing_x + line_width_offset + padding as f32 + x_quant;
925 let y = TEXTURE_SIZE as f32 - dst_y as f32 - rendered_bearing_y - line_width_offset - padding as f32;
926
927 let rendered_glyph = RenderedGlyph {
928 width: width - 2 * GLYPH_MARGIN,
929 height: height - 2 * GLYPH_MARGIN,
930 bearing_y: rendered_bearing_y as i32,
931 atlas_x: dst_x as u32 + GLYPH_MARGIN,
932 atlas_y: dst_y as u32 + GLYPH_MARGIN,
933 texture_index: dst_index,
934 padding: padding - GLYPH_MARGIN,
935 color_glyph,
936 };
937
938 match maybe_glyph_representation.as_mut() {
939 Some(GlyphRendering::RenderAsPath(ref mut path)) => {
940 canvas.translate(x, y);
941
942 canvas.set_render_target(RenderTarget::Image(dst_image_id));
943 canvas.clear_rect(
944 dst_x as u32,
945 TEXTURE_SIZE as u32 - dst_y as u32 - height as u32,
946 width as u32,
947 height as u32,
948 Color::black(),
949 );
950 let factor = 1.0 / 8.0;
951
952 let mut mask_paint = Paint::color(Color::rgbf(factor, factor, factor));
953 mask_paint.set_fill_rule(FillRule::EvenOdd);
954 mask_paint.set_anti_alias(false);
955
956 if mode == RenderMode::Stroke {
957 mask_paint.line_width = line_width / scale;
958 }
959
960 canvas.global_composite_blend_func(crate::BlendFactor::SrcAlpha, crate::BlendFactor::One);
961
962 let points = [
972 (-7.0 / 16.0, -1.0 / 16.0),
973 (-1.0 / 16.0, -5.0 / 16.0),
974 (3.0 / 16.0, -7.0 / 16.0),
975 (5.0 / 16.0, -3.0 / 16.0),
976 (7.0 / 16.0, 1.0 / 16.0),
977 (1.0 / 16.0, 5.0 / 16.0),
978 (-3.0 / 16.0, 7.0 / 16.0),
979 (-5.0 / 16.0, 3.0 / 16.0),
980 ];
981
982 for point in &points {
983 canvas.save();
984 canvas.translate(point.0, point.1);
985
986 canvas.scale(scale, scale);
987
988 if mode == RenderMode::Stroke {
989 canvas.stroke_path(path, mask_paint);
990 } else {
991 canvas.fill_path(path, mask_paint);
992 }
993
994 canvas.restore();
995 }
996 }
997 #[cfg(feature = "image-loading")]
998 Some(GlyphRendering::RenderAsImage(image_buffer)) => {
999 use std::convert::TryFrom;
1000 let target_x = rendered_glyph.atlas_x as usize;
1001 let target_y = rendered_glyph.atlas_y as usize;
1002 let target_width = rendered_glyph.width as u32;
1003 let target_height = rendered_glyph.height as u32;
1004
1005 let image_buffer =
1006 image_buffer.resize(target_width, target_height, image::imageops::FilterType::Nearest);
1007 if let Ok(image) = crate::image::ImageSource::try_from(&image_buffer) {
1008 canvas.update_image(dst_image_id, image, target_x, target_y).unwrap();
1009 }
1010 }
1011 _ => {}
1012 }
1013
1014 canvas.restore();
1015
1016 Ok(rendered_glyph)
1017 }
1018
1019 fn find_texture_or_alloc<T: Renderer>(
1021 &self,
1022 canvas: &mut Canvas<T>,
1023 width: usize,
1024 height: usize,
1025 ) -> Result<(usize, ImageId, (usize, usize)), ErrorKind> {
1026 let mut texture_search_result = {
1028 let mut glyph_textures = self.glyph_textures.borrow_mut();
1029 let mut textures = glyph_textures.iter_mut().enumerate();
1030 textures.find_map(|(index, texture)| {
1031 texture
1032 .atlas
1033 .add_rect(width, height)
1034 .map(|loc| (index, texture.image_id, loc))
1035 })
1036 };
1037
1038 if texture_search_result.is_none() {
1039 let mut atlas = Atlas::new(TEXTURE_SIZE, TEXTURE_SIZE);
1041
1042 let loc = atlas
1043 .add_rect(width, height)
1044 .ok_or(ErrorKind::FontSizeTooLargeForAtlas)?;
1045
1046 let info = ImageInfo::new(ImageFlags::empty(), atlas.size().0, atlas.size().1, PixelFormat::Rgba8);
1052 let image_id = canvas.images.alloc(&mut canvas.renderer, info)?;
1053
1054 #[cfg(feature = "debug_inspector")]
1055 if cfg!(debug_assertions) {
1056 if let Ok(size) = canvas.image_size(image_id) {
1058 #[cfg(feature = "image-loading")]
1063 {
1064 use rgb::FromSlice;
1065 let clear_image = image::RgbaImage::from_pixel(
1066 size.0 as u32,
1067 size.1 as u32,
1068 image::Rgba::<u8>([255, 0, 0, 0]),
1069 );
1070 canvas
1071 .update_image(
1072 image_id,
1073 crate::image::ImageSource::from(imgref::Img::new(
1074 clear_image.as_ref().as_rgba(),
1075 clear_image.width() as usize,
1076 clear_image.height() as usize,
1077 )),
1078 0,
1079 0,
1080 )
1081 .unwrap();
1082 }
1083 #[cfg(not(feature = "image-loading"))]
1084 {
1085 canvas.save();
1086 canvas.reset();
1087 canvas.set_render_target(RenderTarget::Image(image_id));
1088 canvas.clear_rect(
1089 0,
1090 0,
1091 size.0 as u32,
1092 size.1 as u32,
1093 Color::rgb(255, 0, 0), );
1095 canvas.restore();
1096 }
1097 }
1098 }
1099
1100 self.glyph_textures.borrow_mut().push(FontTexture { atlas, image_id });
1101
1102 let index = self.glyph_textures.borrow().len() - 1;
1103 texture_search_result = Some((index, image_id, loc));
1104 }
1105
1106 texture_search_result.ok_or(ErrorKind::UnknownError)
1107 }
1108
1109 pub(crate) fn clear<T: Renderer>(&self, canvas: &mut Canvas<T>) {
1110 let image_ids = std::mem::take(&mut *self.glyph_textures.borrow_mut())
1111 .into_iter()
1112 .map(|font_texture| font_texture.image_id);
1113 image_ids.for_each(|id| canvas.delete_image(id));
1114
1115 self.rendered_glyphs.borrow_mut().clear();
1116 }
1117}
1118
1119pub(crate) fn render_direct<T: Renderer>(
1120 canvas: &mut Canvas<T>,
1121 text_layout: &TextMetrics,
1122 paint: &Paint,
1123 mode: RenderMode,
1124 invscale: f32,
1125) -> Result<(), ErrorKind> {
1126 let mut paint = *paint;
1127 paint.set_fill_rule(FillRule::EvenOdd);
1128
1129 let text_context = canvas.text_context.clone();
1130 let mut text_context = text_context.borrow_mut();
1131
1132 let mut scaled = false;
1133
1134 for glyph in &text_layout.glyphs {
1135 let (glyph_rendering, scale) = {
1136 let font = text_context.font_mut(glyph.font_id).ok_or(ErrorKind::NoFontFound)?;
1137
1138 let scale = font.scale(paint.font_size);
1139
1140 let glyph_rendering = if let Some(glyph_rendering) =
1141 font.glyph_rendering_representation(glyph.codepoint as u16, paint.font_size as u16)
1142 {
1143 glyph_rendering
1144 } else {
1145 continue;
1146 };
1147
1148 (glyph_rendering, scale)
1149 };
1150
1151 canvas.save();
1152
1153 if mode == RenderMode::Stroke && !scaled {
1154 paint.line_width /= scale;
1155 scaled = true;
1156 }
1157
1158 canvas.translate(
1159 (glyph.x - glyph.bearing_x) * invscale,
1160 (glyph.y + glyph.bearing_y) * invscale,
1161 );
1162 canvas.scale(scale * invscale, -scale * invscale);
1163
1164 match glyph_rendering {
1165 GlyphRendering::RenderAsPath(path) => {
1166 if mode == RenderMode::Stroke {
1167 canvas.stroke_path(path, paint);
1168 } else {
1169 canvas.fill_path(path, paint);
1170 }
1171 }
1172 #[cfg(feature = "image-loading")]
1173 GlyphRendering::RenderAsImage(_) => unreachable!(),
1174 }
1175
1176 canvas.restore();
1177 }
1178
1179 Ok(())
1180}