A simple map viewer

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