atlas.rs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2025 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use crate::gfx::{DebugTag, ManagedTexturePtr, Rectangle, RenderApi};
  19. use super::{
  20. ft::{Sprite, SpritePtr},
  21. Glyph,
  22. };
  23. /// Prevents render artifacts from aliasing.
  24. /// Even with aliasing turned off, some bleed still appears possibly
  25. /// due to UV coord calcs. Adding a gap perfectly fixes this.
  26. const ATLAS_GAP: usize = 2;
  27. /// Convenience wrapper fn. Use if rendering a single line of glyphs.
  28. pub fn make_texture_atlas(
  29. render_api: &RenderApi,
  30. tag: DebugTag,
  31. glyphs: &Vec<Glyph>,
  32. ) -> RenderedAtlas {
  33. let mut atlas = Atlas::new(render_api, tag);
  34. atlas.push(&glyphs);
  35. atlas.make()
  36. }
  37. /// Responsible for aggregating glyphs, and then producing a single software
  38. /// blitted texture usable in a single draw call.
  39. /// This makes OpenGL batch precomputation of meshes efficient.
  40. ///
  41. /// ```rust
  42. /// let mut atlas = Atlas::new(&render_api);
  43. /// atlas.push(&glyphs); // repeat as needed for shaped lines
  44. /// let atlas = atlas.make().unwrap();
  45. /// let uv = atlas.fetch_uv(glyph_id).unwrap();
  46. /// let atlas_texture_id = atlas.texture_id;
  47. /// ```
  48. #[derive(Clone)]
  49. pub struct Atlas<'a> {
  50. glyph_ids: Vec<u32>,
  51. sprites: Vec<SpritePtr>,
  52. // LHS x pos of glyph
  53. x_pos: Vec<usize>,
  54. width: usize,
  55. height: usize,
  56. render_api: &'a RenderApi,
  57. tag: DebugTag,
  58. }
  59. impl<'a> Atlas<'a> {
  60. pub fn new(render_api: &'a RenderApi, tag: DebugTag) -> Self {
  61. Self {
  62. glyph_ids: vec![],
  63. sprites: vec![],
  64. x_pos: vec![],
  65. width: ATLAS_GAP,
  66. // Not really important to set a value here since it will
  67. // get overwritten.
  68. // FYI glyphs have a gap on all sides (top and bottom here).
  69. height: 2 * ATLAS_GAP,
  70. render_api,
  71. tag,
  72. }
  73. }
  74. fn push_glyph(&mut self, glyph: &Glyph) {
  75. if self.glyph_ids.contains(&glyph.glyph_id) {
  76. return
  77. }
  78. self.glyph_ids.push(glyph.glyph_id);
  79. self.sprites.push(glyph.sprite.clone());
  80. let sprite = &glyph.sprite;
  81. self.x_pos.push(self.width);
  82. // Gap on the top and bottom
  83. let height = ATLAS_GAP + sprite.bmp_height + ATLAS_GAP;
  84. self.height = std::cmp::max(height, self.height);
  85. // Gap between glyphs and on both sides
  86. self.width += sprite.bmp_width + ATLAS_GAP;
  87. }
  88. /// Push a line of shaped text represented as `Vec<Glyph>`
  89. /// to this atlas.
  90. pub fn push(&mut self, glyphs: &Vec<Glyph>) {
  91. for glyph in glyphs {
  92. self.push_glyph(glyph);
  93. }
  94. }
  95. fn render(&self) -> Vec<u8> {
  96. let mut atlas = vec![0; 4 * self.width * self.height];
  97. // For drawing debug lines we want a single white pixel.
  98. // This is very useful to have in our texture for debugging.
  99. atlas[0] = 255;
  100. atlas[1] = 255;
  101. atlas[2] = 255;
  102. atlas[3] = 255;
  103. let y = ATLAS_GAP;
  104. // Copy all the sprites to our atlas.
  105. // They should have ATLAS_GAP spacing on all sides to avoid bleeding.
  106. for (sprite, x) in self.sprites.iter().zip(self.x_pos.iter()) {
  107. copy_image(sprite, *x, y, &mut atlas, self.width);
  108. }
  109. atlas
  110. }
  111. fn compute_uvs(&self) -> Vec<Rectangle> {
  112. // UV coords are in the range [0, 1]
  113. let mut uvs = vec![];
  114. let (self_w, self_h) = (self.width as f32, self.height as f32);
  115. let y = ATLAS_GAP as f32;
  116. for (sprite, x) in self.sprites.iter().zip(self.x_pos.iter()) {
  117. let x = *x as f32;
  118. let sprite_w = sprite.bmp_width as f32;
  119. let sprite_h = sprite.bmp_height as f32;
  120. let uv = Rectangle {
  121. x: x / self_w,
  122. y: y / self_h,
  123. w: sprite_w / self_w,
  124. h: sprite_h / self_h,
  125. };
  126. uvs.push(uv);
  127. }
  128. uvs
  129. }
  130. /// Invalidate this atlas and produce the finalized result.
  131. /// Each glyph is given a sub-rect within the texture, accessible by calling
  132. /// `rendered_atlas.fetch_uv(my_glyph_id)`.
  133. /// The texture ID is a struct member: `rendered_atlas.texture_id`.
  134. pub fn make(self) -> RenderedAtlas {
  135. //if self.glyph_ids.is_empty() {
  136. // return Err(Error::AtlasIsEmpty)
  137. //}
  138. assert_eq!(self.glyph_ids.len(), self.sprites.len());
  139. assert_eq!(self.glyph_ids.len(), self.x_pos.len());
  140. let atlas = self.render();
  141. let texture =
  142. self.render_api.new_texture(self.width as u16, self.height as u16, atlas, self.tag);
  143. let uv_rects = self.compute_uvs();
  144. let glyph_ids = self.glyph_ids;
  145. RenderedAtlas { glyph_ids, uv_rects, texture }
  146. }
  147. }
  148. /// Copy a sprite to (x, y) position within the atlas texture.
  149. /// Both image formats are RGBA flat vecs.
  150. fn copy_image(sprite: &Sprite, x: usize, y: usize, atlas: &mut Vec<u8>, atlas_width: usize) {
  151. for i in 0..sprite.bmp_height {
  152. for j in 0..sprite.bmp_width {
  153. let src_y = i * sprite.bmp_width;
  154. let off_src = 4 * (src_y + j);
  155. let dest_y = (y + i) * atlas_width;
  156. let off_dest = 4 * (dest_y + j + x);
  157. atlas[off_dest] = sprite.bmp[off_src];
  158. atlas[off_dest + 1] = sprite.bmp[off_src + 1];
  159. atlas[off_dest + 2] = sprite.bmp[off_src + 2];
  160. atlas[off_dest + 3] = sprite.bmp[off_src + 3];
  161. }
  162. }
  163. }
  164. /// Final result computed from `Atlas::make()`.
  165. #[derive(Clone)]
  166. pub struct RenderedAtlas {
  167. glyph_ids: Vec<u32>,
  168. /// UV rectangle within the texture.
  169. uv_rects: Vec<Rectangle>,
  170. /// Allocated atlas texture.
  171. pub texture: ManagedTexturePtr,
  172. }
  173. impl RenderedAtlas {
  174. /// Get UV coords for a glyph within the rendered atlas.
  175. pub fn fetch_uv(&self, glyph_id: u32) -> Option<&Rectangle> {
  176. let glyphs_len = self.glyph_ids.len();
  177. assert_eq!(glyphs_len, self.uv_rects.len());
  178. for i in 0..glyphs_len {
  179. if self.glyph_ids[i] == glyph_id {
  180. return Some(&self.uv_rects[i])
  181. }
  182. }
  183. None
  184. }
  185. }