A simple map viewer

tile_atlas.rs 9.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. use coord::{ScreenRect, SubTileCoord, TileCoord, TextureRect};
  2. use image;
  3. use linked_hash_map::LinkedHashMap;
  4. use map_view::VisibleTile;
  5. use std::collections::HashMap;
  6. use std::collections::hash_map::Entry;
  7. use texture::Texture;
  8. use tile::Tile;
  9. use tile_cache::TileCache;
  10. use tile_source::TileSource;
  11. #[derive(Clone, Debug)]
  12. pub struct TexturedVisibleTile {
  13. pub screen_rect: ScreenRect,
  14. pub tex_rect: TextureRect,
  15. pub tex_minmax: TextureRect,
  16. }
  17. #[derive(Clone, Debug)]
  18. pub struct TileAtlas<'a> {
  19. texture: Texture<'a>,
  20. tile_size: u32,
  21. slots_lru: LinkedHashMap<CacheSlot, Option<Tile>>, // LRU cache of slots
  22. tile_to_slot: HashMap<Tile, CacheSlot>,
  23. use_async: bool,
  24. }
  25. impl<'a> TileAtlas<'a> {
  26. fn init(&mut self) {
  27. // add tile for default slot
  28. {
  29. let img = image::load_from_memory(
  30. include_bytes!("../img/no_tile.png"),
  31. ).unwrap();
  32. self.texture.sub_image(0, 0, &img);
  33. }
  34. let slots_x = self.texture.width() / self.tile_size;
  35. let slots_y = self.texture.height() / self.tile_size;
  36. let num_slots = (slots_x * slots_y) as usize;
  37. self.slots_lru.clear();
  38. self.slots_lru.reserve(num_slots);
  39. for x in 0..slots_x {
  40. for y in 0..slots_y {
  41. let slot = CacheSlot { x, y };
  42. self.slots_lru.insert(slot, None);
  43. }
  44. }
  45. self.slots_lru.remove(&Self::default_slot());
  46. self.tile_to_slot.clear();
  47. self.tile_to_slot.reserve(num_slots);
  48. }
  49. pub fn new(tex: Texture<'a>, tile_size: u32, use_async: bool) -> Self {
  50. let mut atlas = TileAtlas {
  51. texture: tex,
  52. tile_size,
  53. slots_lru: LinkedHashMap::new(),
  54. tile_to_slot: HashMap::new(),
  55. use_async,
  56. };
  57. atlas.init();
  58. atlas
  59. }
  60. pub fn double_texture_size(&mut self) -> Result<(), ()> {
  61. let max_size = self.texture.context().max_texture_size() as u32;
  62. let new_width = self.texture.width() * 2;
  63. let new_height = self.texture.height() * 2;
  64. if new_width <= max_size && new_height <= max_size {
  65. self.texture.resize(new_width, new_height);
  66. // remove old entries, initialize texture
  67. self.init();
  68. info!("new atlas size {}x{}", new_width, new_height);
  69. Ok(())
  70. } else {
  71. Err(())
  72. }
  73. }
  74. pub fn default_slot() -> CacheSlot {
  75. CacheSlot { x: 0, y: 0 }
  76. }
  77. pub fn store(&mut self, tile_coord: TileCoord, source: &TileSource, cache: &mut TileCache, load: bool) -> Option<CacheSlot> {
  78. let mut remove_tile = None;
  79. let tile = Tile::new(tile_coord, source.id());
  80. let slot = match self.tile_to_slot.entry(tile) {
  81. Entry::Vacant(entry) => {
  82. let img_option = if load {
  83. if self.use_async {
  84. cache.get_async(tile_coord, source, true)
  85. } else {
  86. cache.get_sync(tile_coord, source, true)
  87. }
  88. } else {
  89. cache.lookup(tile)
  90. };
  91. if let Some(img) = img_option {
  92. let (slot, old_tile) = self.slots_lru.pop_front().unwrap();
  93. self.slots_lru.insert(slot, Some(tile));
  94. remove_tile = old_tile;
  95. self.texture.sub_image(
  96. (slot.x * self.tile_size) as i32,
  97. (slot.y * self.tile_size) as i32,
  98. img,
  99. );
  100. Some(*entry.insert(slot))
  101. } else {
  102. None
  103. }
  104. },
  105. Entry::Occupied(entry) => {
  106. let slot = *entry.into_mut();
  107. self.slots_lru.get_refresh(&slot);
  108. Some(slot)
  109. },
  110. };
  111. if let Some(t) = remove_tile {
  112. self.tile_to_slot.remove(&t);
  113. }
  114. slot
  115. }
  116. /// Finds textures from the cache for a given slice of visible tiles. The texture atlas may not
  117. /// be big enough to hold all textures at once; a possible remainder of untextured visible tiles is
  118. /// returned as an `Option`.
  119. /// The function guarantees that no more than `max_tiles_to_use` tiles are used for texturing;
  120. /// the number of used tiles is returned as an `usize`.
  121. pub fn textured_visible_tiles<'b>(
  122. &mut self,
  123. visible_tiles: &'b [VisibleTile],
  124. max_tiles_to_use: usize,
  125. source: &TileSource,
  126. cache: &mut TileCache,
  127. ) -> (Vec<TexturedVisibleTile>, Option<&'b [VisibleTile]>, usize)
  128. {
  129. let mut tvt = Vec::with_capacity(visible_tiles.len());
  130. let inset_x = 0.5 / f64::from(self.texture.width());
  131. let inset_y = 0.5 / f64::from(self.texture.height());
  132. let num_usable_slots = self.slots_lru.len();
  133. // The number of actually used slots may be lower, because slots can be used multiple times
  134. // in the same view (especially the default slot).
  135. let mut used_slots = 0_usize;
  136. for (i, vt) in visible_tiles.iter().enumerate() {
  137. if used_slots >= num_usable_slots || used_slots >= max_tiles_to_use {
  138. return (tvt, Some(&visible_tiles[i..]), used_slots);
  139. }
  140. if let Some(slot) = self.store(vt.tile, source, cache, true) {
  141. let tex_rect = self.slot_to_texture_rect(slot);
  142. used_slots += 1;
  143. tvt.push(
  144. TexturedVisibleTile {
  145. screen_rect: vt.rect,
  146. tex_rect,
  147. tex_minmax: tex_rect.inset(inset_x, inset_y),
  148. }
  149. );
  150. } else {
  151. // exact tile not found
  152. if used_slots + 5 > num_usable_slots || used_slots + 5 > max_tiles_to_use {
  153. return (tvt, Some(&visible_tiles[i..]), used_slots);
  154. }
  155. // default tile
  156. let mut tex_sub_rect = self.slot_to_texture_rect(Self::default_slot());
  157. let mut tex_rect = tex_sub_rect;
  158. // look for cached tiles in lower zoom layers
  159. for dist in 1..31 {
  160. if let Some((parent_tile, sub_coord)) = vt.tile.parent(dist) {
  161. if let Some(slot) = self.store(parent_tile, source, cache, false) {
  162. used_slots += 1;
  163. tex_sub_rect = self.subslot_to_texture_rect(slot, sub_coord);
  164. tex_rect = self.slot_to_texture_rect(slot);
  165. break;
  166. }
  167. } else {
  168. break;
  169. }
  170. }
  171. // look for cached tiles in higher zoom layers
  172. for &(child_tile, child_sub_coord) in &vt.tile.children() {
  173. if let Some(slot) = self.store(child_tile, source, cache, false) {
  174. used_slots += 1;
  175. let tex_rect = self.slot_to_texture_rect(slot);
  176. tvt.push(
  177. TexturedVisibleTile {
  178. screen_rect: vt.rect.subdivide(&child_sub_coord),
  179. tex_rect,
  180. tex_minmax: tex_rect.inset(inset_x, inset_y),
  181. }
  182. );
  183. } else {
  184. tvt.push(
  185. TexturedVisibleTile {
  186. screen_rect: vt.rect.subdivide(&child_sub_coord),
  187. tex_rect: tex_sub_rect.subdivide(&child_sub_coord),
  188. tex_minmax: tex_rect.inset(inset_x, inset_y),
  189. }
  190. );
  191. }
  192. }
  193. };
  194. }
  195. (tvt, None, used_slots)
  196. }
  197. fn slot_to_texture_rect(&self, slot: CacheSlot) -> TextureRect {
  198. let scale_x = f64::from(self.tile_size) / f64::from(self.texture.width());
  199. let scale_y = f64::from(self.tile_size) / f64::from(self.texture.height());
  200. TextureRect {
  201. x1: f64::from(slot.x) * scale_x,
  202. y1: f64::from(slot.y) * scale_y,
  203. x2: f64::from(slot.x + 1) * scale_x,
  204. y2: f64::from(slot.y + 1) * scale_y,
  205. }
  206. }
  207. fn subslot_to_texture_rect(&self, slot: CacheSlot, sub_coord: SubTileCoord) -> TextureRect {
  208. let scale_x = f64::from(self.tile_size) / (f64::from(self.texture.width()) * f64::from(sub_coord.size));
  209. let scale_y = f64::from(self.tile_size) / (f64::from(self.texture.height()) * f64::from(sub_coord.size));
  210. TextureRect {
  211. x1: f64::from(slot.x * sub_coord.size + sub_coord.x) * scale_x,
  212. y1: f64::from(slot.y * sub_coord.size + sub_coord.y) * scale_y,
  213. x2: f64::from(slot.x * sub_coord.size + sub_coord.x + 1) * scale_x,
  214. y2: f64::from(slot.y * sub_coord.size + sub_coord.y + 1) * scale_y,
  215. }
  216. }
  217. }
  218. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
  219. pub struct CacheSlot {
  220. pub x: u32,
  221. pub y: u32,
  222. }