atlas.rs 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  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::{
  19. error::Result,
  20. gfx::{GfxTextureId, ManagedTexturePtr, Rectangle, RenderApi},
  21. mesh::Color,
  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. /*
  28. /// Convenience wrapper fn. Use if rendering a single line of glyphs.
  29. pub fn make_texture_atlas(render_api: &RenderApi, glyphs: &Vec<Glyph>) -> RenderedAtlas {
  30. let mut atlas = Atlas::new(render_api);
  31. atlas.push(&glyphs);
  32. atlas.make()
  33. }
  34. */
  35. //pub struct Sprite(swash::scale::image::Image);
  36. /// Responsible for aggregating glyphs, and then producing a single software
  37. /// blitted texture usable in a single draw call.
  38. /// This makes OpenGL batch precomputation of meshes efficient.
  39. ///
  40. /// ```rust
  41. /// let mut atlas = Atlas::new(&render_api);
  42. /// atlas.push(&glyphs); // repeat as needed for shaped lines
  43. /// let atlas = atlas.make().unwrap();
  44. /// let uv = atlas.fetch_uv(glyph_id).unwrap();
  45. /// let atlas_texture_id = atlas.texture_id;
  46. /// ```
  47. pub struct Atlas<'a> {
  48. scaler: swash::scale::Scaler<'a>,
  49. glyph_ids: Vec<swash::GlyphId>,
  50. sprites: Vec<swash::scale::image::Image>,
  51. // LHS x pos of glyph
  52. x_pos: Vec<usize>,
  53. width: usize,
  54. height: usize,
  55. render_api: &'a RenderApi,
  56. }
  57. impl<'a> Atlas<'a> {
  58. pub fn new(scaler: swash::scale::Scaler<'a>, render_api: &'a RenderApi) -> Self {
  59. Self {
  60. scaler,
  61. glyph_ids: vec![],
  62. sprites: vec![],
  63. x_pos: vec![],
  64. width: ATLAS_GAP,
  65. // Not really important to set a value here since it will
  66. // get overwritten.
  67. // FYI glyphs have a gap on all sides (top and bottom here).
  68. height: 2 * ATLAS_GAP,
  69. render_api,
  70. }
  71. }
  72. pub fn push_glyph(&mut self, glyph: parley::Glyph) {
  73. if self.glyph_ids.contains(&glyph.id) {
  74. return
  75. }
  76. self.glyph_ids.push(glyph.id);
  77. let rendered_glyph = swash::scale::Render::new(
  78. // Select our source order
  79. &[
  80. swash::scale::Source::ColorOutline(0),
  81. swash::scale::Source::ColorBitmap(swash::scale::StrikeWith::BestFit),
  82. swash::scale::Source::Outline,
  83. ],
  84. )
  85. // Select the simple alpha (non-subpixel) format
  86. .format(zeno::Format::Alpha)
  87. .render(&mut self.scaler, glyph.id)
  88. .unwrap();
  89. let glyph_width = rendered_glyph.placement.width as usize;
  90. let glyph_height = rendered_glyph.placement.height as usize;
  91. self.sprites.push(rendered_glyph);
  92. self.x_pos.push(self.width);
  93. // Gap on the top and bottom
  94. let height = ATLAS_GAP + glyph_height + ATLAS_GAP;
  95. self.height = std::cmp::max(height, self.height);
  96. // Gap between glyphs and on both sides
  97. self.width += glyph_width + ATLAS_GAP;
  98. }
  99. fn render(&self) -> Vec<u8> {
  100. let mut atlas = vec![255, 255, 255, 0].repeat(self.width * self.height);
  101. // For drawing debug lines we want a single white pixel.
  102. // This is very useful to have in our texture for debugging.
  103. atlas[0] = 255;
  104. atlas[1] = 255;
  105. atlas[2] = 255;
  106. atlas[3] = 255;
  107. let y = ATLAS_GAP;
  108. // Copy all the sprites to our atlas.
  109. // They should have ATLAS_GAP spacing on all sides to avoid bleeding.
  110. for (sprite, x) in self.sprites.iter().zip(self.x_pos.iter()) {
  111. copy_image(sprite, *x, y, &mut atlas, self.width);
  112. }
  113. atlas
  114. }
  115. fn compute_uvs(&self) -> Vec<Rectangle> {
  116. // UV coords are in the range [0, 1]
  117. let mut uvs = Vec::with_capacity(self.sprites.len());
  118. let (self_w, self_h) = (self.width as f32, self.height as f32);
  119. let y = ATLAS_GAP as f32;
  120. for (sprite, x) in self.sprites.iter().zip(self.x_pos.iter()) {
  121. let x = *x as f32;
  122. let sprite_w = sprite.placement.width as f32;
  123. let sprite_h = sprite.placement.height as f32;
  124. let uv = Rectangle {
  125. x: x / self_w,
  126. y: y / self_h,
  127. w: sprite_w / self_w,
  128. h: sprite_h / self_h,
  129. };
  130. uvs.push(uv);
  131. }
  132. uvs
  133. }
  134. /// Debug method
  135. pub fn dump(&self, output_path: &str) {
  136. let atlas = self.render();
  137. let img = image::RgbaImage::from_raw(self.width as u32, self.height as u32, atlas).unwrap();
  138. img.save(output_path);
  139. }
  140. /// Invalidate this atlas and produce the finalized result.
  141. /// Each glyph is given a sub-rect within the texture, accessible by calling
  142. /// `rendered_atlas.fetch_uv(my_glyph_id)`.
  143. /// The texture ID is a struct member: `rendered_atlas.texture_id`.
  144. pub fn make(self) -> RenderedAtlas {
  145. //if self.glyph_ids.is_empty() {
  146. // return Err(Error::AtlasIsEmpty)
  147. //}
  148. assert_eq!(self.glyph_ids.len(), self.sprites.len());
  149. assert_eq!(self.glyph_ids.len(), self.x_pos.len());
  150. let atlas = self.render();
  151. let texture = self.render_api.new_texture(self.width as u16, self.height as u16, atlas);
  152. let uv_rects = self.compute_uvs();
  153. let glyph_ids = self.glyph_ids;
  154. let mut infos = Vec::with_capacity(self.sprites.len());
  155. for (uv_rect, sprite) in uv_rects.into_iter().zip(self.sprites.into_iter()) {
  156. let is_color = match sprite.content {
  157. swash::scale::image::Content::Mask => false,
  158. swash::scale::image::Content::SubpixelMask => unimplemented!(),
  159. swash::scale::image::Content::Color => true,
  160. };
  161. infos.push(GlyphInfo { uv_rect, place: sprite.placement, is_color });
  162. }
  163. RenderedAtlas { glyph_ids, infos, texture }
  164. }
  165. }
  166. /// Copy a sprite to (x, y) position within the atlas texture.
  167. /// Both image formats are RGBA flat vecs.
  168. fn copy_image(
  169. sprite: &swash::scale::image::Image,
  170. x: usize,
  171. y: usize,
  172. atlas: &mut Vec<u8>,
  173. atlas_width: usize,
  174. ) {
  175. let sprite_width = sprite.placement.width as usize;
  176. let sprite_height = sprite.placement.height as usize;
  177. match sprite.content {
  178. swash::scale::image::Content::Mask => {
  179. let mut i = 0;
  180. for pixel_y in 0..sprite_height {
  181. for pixel_x in 0..sprite_width {
  182. let src_alpha = sprite.data[i];
  183. let dest_y = (y + pixel_y) * atlas_width;
  184. let off_dest = 4 * (dest_y + pixel_x + x);
  185. //atlas[off_dest] = 255;
  186. //atlas[off_dest + 1] = 255;
  187. //atlas[off_dest + 2] = 255;
  188. atlas[off_dest + 3] = src_alpha;
  189. i += 1;
  190. }
  191. }
  192. }
  193. swash::scale::image::Content::SubpixelMask => unimplemented!(),
  194. swash::scale::image::Content::Color => {
  195. let row_size = sprite_width * 4;
  196. for (pixel_y, row) in sprite.data.chunks_exact(row_size).enumerate() {
  197. for (pixel_x, pixel) in row.chunks_exact(4).enumerate() {
  198. assert_eq!(pixel.len(), 4);
  199. let src_y = pixel_y * sprite_width;
  200. let off_src = 4 * (src_y + pixel_x);
  201. let dest_y = (y + pixel_y) * atlas_width;
  202. let off_dest = 4 * (dest_y + pixel_x + x);
  203. atlas[off_dest] = pixel[0];
  204. atlas[off_dest + 1] = pixel[1];
  205. atlas[off_dest + 2] = pixel[2];
  206. atlas[off_dest + 3] = pixel[3];
  207. }
  208. }
  209. }
  210. }
  211. }
  212. #[derive(Clone)]
  213. pub struct GlyphInfo {
  214. /// UV rectangle within the texture.
  215. pub uv_rect: Rectangle,
  216. /// Placement of the sprite used to calc the rect
  217. pub place: zeno::Placement,
  218. pub is_color: bool,
  219. }
  220. /// Final result computed from `Atlas::make()`.
  221. #[derive(Clone)]
  222. pub struct RenderedAtlas {
  223. glyph_ids: Vec<swash::GlyphId>,
  224. infos: Vec<GlyphInfo>,
  225. /// Allocated atlas texture.
  226. pub texture: ManagedTexturePtr,
  227. }
  228. impl RenderedAtlas {
  229. /// Get UV coords for a glyph within the rendered atlas.
  230. pub fn fetch_uv(&self, glyph_id: swash::GlyphId) -> Option<&GlyphInfo> {
  231. let glyphs_len = self.glyph_ids.len();
  232. assert_eq!(glyphs_len, self.infos.len());
  233. for i in 0..glyphs_len {
  234. if self.glyph_ids[i] == glyph_id {
  235. return Some(&self.infos[i])
  236. }
  237. }
  238. None
  239. }
  240. }