A simple map viewer

tile_cache_gl.rs 6.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. use coord::{ScreenRect, SubTileCoord, TileCoord, TextureRect};
  2. use linked_hash_map::LinkedHashMap;
  3. use map_view::VisibleTile;
  4. use std::collections::HashMap;
  5. use std::collections::hash_map::Entry;
  6. use texture::Texture;
  7. use tile::Tile;
  8. use tile_cache::TileCache;
  9. use tile_source::TileSource;
  10. #[derive(Clone, Debug)]
  11. pub struct TexturedVisibleTile {
  12. pub screen_rect: ScreenRect,
  13. pub tex_rect: TextureRect,
  14. pub tex_minmax: TextureRect,
  15. }
  16. #[derive(Clone, Debug)]
  17. pub struct TileCacheGl<'a> {
  18. texture: Texture<'a>,
  19. tile_size: u32,
  20. slots_lru: LinkedHashMap<CacheSlot, Option<Tile>>, // LRU cache of slots
  21. tile_to_slot: HashMap<Tile, CacheSlot>,
  22. }
  23. impl<'a> TileCacheGl<'a> {
  24. pub fn new(tex: Texture<'a>, tile_size: u32) -> Self {
  25. let slots_x = tex.width() / tile_size;
  26. let slots_y = tex.height() / tile_size;
  27. let num_slots = (slots_x * slots_y) as usize;
  28. let mut slots_lru = LinkedHashMap::with_capacity(num_slots);
  29. for x in 0..slots_x {
  30. for y in 0..slots_y {
  31. let slot = CacheSlot { x: x, y: y };
  32. slots_lru.insert(slot, None);
  33. }
  34. }
  35. slots_lru.remove(&Self::default_slot());
  36. TileCacheGl {
  37. texture: tex,
  38. tile_size: tile_size,
  39. slots_lru: slots_lru,
  40. tile_to_slot: HashMap::with_capacity(num_slots),
  41. }
  42. }
  43. pub fn default_slot() -> CacheSlot {
  44. CacheSlot { x: 0, y: 0 }
  45. }
  46. pub fn store(&mut self, tile_coord: TileCoord, source: &TileSource, cache: &mut TileCache, load: bool) -> Option<CacheSlot> {
  47. let mut remove_tile = None;
  48. let tile = Tile::new(tile_coord, source.id());
  49. let slot = match self.tile_to_slot.entry(tile) {
  50. Entry::Vacant(entry) => {
  51. let img_option = if load {
  52. cache.get_async(tile_coord, source, true)
  53. } else {
  54. cache.lookup(tile)
  55. };
  56. if let Some(img) = img_option {
  57. let (slot, old_tile) = self.slots_lru.pop_front().unwrap();
  58. self.slots_lru.insert(slot, Some(tile));
  59. remove_tile = old_tile;
  60. self.texture.sub_image(
  61. (slot.x * self.tile_size) as i32,
  62. (slot.y * self.tile_size) as i32,
  63. img,
  64. );
  65. Some(*entry.insert(slot))
  66. } else {
  67. None
  68. }
  69. },
  70. Entry::Occupied(entry) => {
  71. let slot = *entry.into_mut();
  72. self.slots_lru.get_refresh(&slot);
  73. Some(slot)
  74. },
  75. };
  76. if let Some(t) = remove_tile {
  77. self.tile_to_slot.remove(&t);
  78. }
  79. slot
  80. }
  81. pub fn textured_visible_tiles(
  82. &mut self,
  83. visible_tiles: &[VisibleTile],
  84. source: &TileSource,
  85. cache: &mut TileCache,
  86. ) -> Vec<TexturedVisibleTile>
  87. {
  88. let mut tvt = Vec::with_capacity(visible_tiles.len());
  89. let inset_x = 0.5 / f64::from(self.texture.width());
  90. let inset_y = 0.5 / f64::from(self.texture.height());
  91. for vt in visible_tiles {
  92. if let Some(slot) = self.store(vt.tile, source, cache, true) {
  93. let tex_rect = self.slot_to_texture_rect(slot);
  94. tvt.push(
  95. TexturedVisibleTile {
  96. screen_rect: vt.rect,
  97. tex_rect: tex_rect,
  98. tex_minmax: tex_rect.inset(inset_x, inset_y),
  99. }
  100. );
  101. } else {
  102. // exact tile not found
  103. // default tile
  104. let mut tex_sub_rect = self.slot_to_texture_rect(Self::default_slot());
  105. let mut tex_rect = tex_sub_rect;
  106. // look for cached tiles in lower zoom layers
  107. for dist in 1..31 {
  108. if let Some((parent_tile, sub_coord)) = vt.tile.parent(dist) {
  109. if let Some(slot) = self.store(parent_tile, source, cache, false) {
  110. tex_sub_rect = self.subslot_to_texture_rect(slot, sub_coord);
  111. tex_rect = self.slot_to_texture_rect(slot);
  112. break;
  113. }
  114. } else {
  115. break;
  116. }
  117. }
  118. // look for cached tiles in higher zoom layers
  119. for &(child_tile, child_sub_coord) in &vt.tile.children() {
  120. if let Some(slot) = self.store(child_tile, source, cache, false) {
  121. let tex_rect = self.slot_to_texture_rect(slot);
  122. tvt.push(
  123. TexturedVisibleTile {
  124. screen_rect: vt.rect.subdivide(&child_sub_coord),
  125. tex_rect: tex_rect,
  126. tex_minmax: tex_rect.inset(inset_x, inset_y),
  127. }
  128. );
  129. } else {
  130. tvt.push(
  131. TexturedVisibleTile {
  132. screen_rect: vt.rect.subdivide(&child_sub_coord),
  133. tex_rect: tex_sub_rect.subdivide(&child_sub_coord),
  134. tex_minmax: tex_rect.inset(inset_x, inset_y),
  135. }
  136. );
  137. }
  138. }
  139. };
  140. }
  141. tvt
  142. }
  143. fn slot_to_texture_rect(&self, slot: CacheSlot) -> TextureRect {
  144. let scale_x = f64::from(self.tile_size) / f64::from(self.texture.width());
  145. let scale_y = f64::from(self.tile_size) / f64::from(self.texture.height());
  146. TextureRect {
  147. x1: f64::from(slot.x) * scale_x,
  148. y1: f64::from(slot.y) * scale_y,
  149. x2: f64::from(slot.x + 1) * scale_x,
  150. y2: f64::from(slot.y + 1) * scale_y,
  151. }
  152. }
  153. fn subslot_to_texture_rect(&self, slot: CacheSlot, sub_coord: SubTileCoord) -> TextureRect {
  154. let scale_x = f64::from(self.tile_size) / (f64::from(self.texture.width()) * f64::from(sub_coord.size));
  155. let scale_y = f64::from(self.tile_size) / (f64::from(self.texture.height()) * f64::from(sub_coord.size));
  156. TextureRect {
  157. x1: f64::from(slot.x * sub_coord.size + sub_coord.x) * scale_x,
  158. y1: f64::from(slot.y * sub_coord.size + sub_coord.y) * scale_y,
  159. x2: f64::from(slot.x * sub_coord.size + sub_coord.x + 1) * scale_x,
  160. y2: f64::from(slot.y * sub_coord.size + sub_coord.y + 1) * scale_y,
  161. }
  162. }
  163. }
  164. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
  165. pub struct CacheSlot {
  166. pub x: u32,
  167. pub y: u32,
  168. }