A simple map viewer

tile_atlas.rs 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. use context::Context;
  2. use coord::{SubTileCoord, TileCoord, TextureRect};
  3. use image;
  4. use linked_hash_map::LinkedHashMap;
  5. use mercator_view;
  6. use orthografic_view;
  7. use std::collections::HashMap;
  8. use std::collections::hash_map::Entry;
  9. use texture::Texture;
  10. use tile::Tile;
  11. use tile_cache::TileCache;
  12. use tile_source::TileSource;
  13. #[derive(Clone, Debug)]
  14. pub struct TileAtlas {
  15. texture: Texture,
  16. tile_size: u32,
  17. slots_lru: LinkedHashMap<CacheSlot, Option<Tile>>, // LRU cache of slots
  18. tile_to_slot: HashMap<Tile, CacheSlot>,
  19. use_async: bool,
  20. }
  21. impl TileAtlas {
  22. fn init(&mut self, cx: &mut Context) {
  23. // add tile for default slot
  24. {
  25. let img = image::load_from_memory(
  26. include_bytes!("../img/no_tile.png"),
  27. ).unwrap();
  28. self.texture.sub_image(cx, 0, 0, &img);
  29. }
  30. let slots_x = self.texture.width() / self.tile_size;
  31. let slots_y = self.texture.height() / self.tile_size;
  32. let num_slots = (slots_x * slots_y) as usize;
  33. self.slots_lru.clear();
  34. self.slots_lru.reserve(num_slots);
  35. for x in 0..slots_x {
  36. for y in 0..slots_y {
  37. let slot = CacheSlot { x, y };
  38. self.slots_lru.insert(slot, None);
  39. }
  40. }
  41. self.slots_lru.remove(&Self::default_slot());
  42. self.tile_to_slot.clear();
  43. self.tile_to_slot.reserve(num_slots);
  44. }
  45. pub fn new(cx: &mut Context, tex: Texture, tile_size: u32, use_async: bool) -> Self {
  46. let mut atlas = TileAtlas {
  47. texture: tex,
  48. tile_size,
  49. slots_lru: LinkedHashMap::new(),
  50. tile_to_slot: HashMap::new(),
  51. use_async,
  52. };
  53. atlas.init(cx);
  54. atlas
  55. }
  56. pub fn double_texture_size(&mut self, cx: &mut Context) -> Result<(), ()> {
  57. let max_size = cx.max_texture_size() as u32;
  58. let new_width = self.texture.width() * 2;
  59. let new_height = self.texture.height() * 2;
  60. if new_width <= max_size && new_height <= max_size {
  61. self.texture.resize(cx, new_width, new_height);
  62. // remove old entries, initialize texture
  63. self.init(cx);
  64. info!("new atlas size {}x{}", new_width, new_height);
  65. Ok(())
  66. } else {
  67. Err(())
  68. }
  69. }
  70. pub fn default_slot() -> CacheSlot {
  71. CacheSlot { x: 0, y: 0 }
  72. }
  73. pub fn store(
  74. &mut self,
  75. cx: &mut Context,
  76. tile_coord: TileCoord,
  77. source: &TileSource,
  78. cache: &mut TileCache,
  79. load: bool
  80. ) -> Option<CacheSlot> {
  81. let mut remove_tile = None;
  82. let tile = Tile::new(tile_coord, source.id());
  83. let slot = match self.tile_to_slot.entry(tile) {
  84. Entry::Vacant(entry) => {
  85. let img_option = if load {
  86. if self.use_async {
  87. cache.get_async(tile_coord, source, true)
  88. } else {
  89. cache.get_sync(tile_coord, source, true)
  90. }
  91. } else {
  92. cache.lookup(tile)
  93. };
  94. if let Some(img) = img_option {
  95. let (slot, old_tile) = self.slots_lru.pop_front().unwrap();
  96. self.slots_lru.insert(slot, Some(tile));
  97. remove_tile = old_tile;
  98. self.texture.sub_image(
  99. cx,
  100. (slot.x * self.tile_size) as i32,
  101. (slot.y * self.tile_size) as i32,
  102. img,
  103. );
  104. Some(*entry.insert(slot))
  105. } else {
  106. None
  107. }
  108. },
  109. Entry::Occupied(entry) => {
  110. let slot = *entry.into_mut();
  111. self.slots_lru.get_refresh(&slot);
  112. Some(slot)
  113. },
  114. };
  115. if let Some(t) = remove_tile {
  116. self.tile_to_slot.remove(&t);
  117. }
  118. slot
  119. }
  120. /// Return 0.5 pixels in texture coordinates for both dimensions.
  121. pub fn texture_margins(&self) -> (f64, f64) {
  122. (0.5 / f64::from(self.texture.width()),
  123. 0.5 / f64::from(self.texture.height()))
  124. }
  125. pub fn slot_to_texture_rect(&self, slot: CacheSlot) -> TextureRect {
  126. let scale_x = f64::from(self.tile_size) / f64::from(self.texture.width());
  127. let scale_y = f64::from(self.tile_size) / f64::from(self.texture.height());
  128. TextureRect {
  129. x1: f64::from(slot.x) * scale_x,
  130. y1: f64::from(slot.y) * scale_y,
  131. x2: f64::from(slot.x + 1) * scale_x,
  132. y2: f64::from(slot.y + 1) * scale_y,
  133. }
  134. }
  135. fn subslot_to_texture_rect(&self, slot: CacheSlot, sub_coord: SubTileCoord) -> TextureRect {
  136. let scale_x = f64::from(self.tile_size) / (f64::from(self.texture.width()) *
  137. f64::from(sub_coord.size));
  138. let scale_y = f64::from(self.tile_size) / (f64::from(self.texture.height()) *
  139. f64::from(sub_coord.size));
  140. TextureRect {
  141. x1: f64::from(slot.x * sub_coord.size + sub_coord.x) * scale_x,
  142. y1: f64::from(slot.y * sub_coord.size + sub_coord.y) * scale_y,
  143. x2: f64::from(slot.x * sub_coord.size + sub_coord.x + 1) * scale_x,
  144. y2: f64::from(slot.y * sub_coord.size + sub_coord.y + 1) * scale_y,
  145. }
  146. }
  147. pub fn texture(&self) -> &Texture {
  148. &self.texture
  149. }
  150. }
  151. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
  152. pub struct CacheSlot {
  153. pub x: u32,
  154. pub y: u32,
  155. }
  156. /// U: Untextured Visible tile type
  157. /// T: Textured visible tile type
  158. pub trait VisibleTilesProvider<U, T> {
  159. /// Finds textures from the cache for a given slice of visible tiles. The texture atlas may not
  160. /// be big enough to hold all textures at once; a possible remainder of untextured visible
  161. /// tiles is returned as an `Option`.
  162. /// The function should guarantee that no more than `max_tiles_to_use` tiles are used for texturing;
  163. /// the number of used tiles is returned as an `usize`.
  164. fn textured_visible_tiles<'b>(
  165. &mut self,
  166. cx: &mut Context,
  167. visible_tiles: &'b [U],
  168. max_tiles_to_use: usize,
  169. source: &TileSource,
  170. cache: &mut TileCache,
  171. ) -> (Vec<T>, Option<&'b [U]>, usize);
  172. }
  173. impl VisibleTilesProvider<mercator_view::VisibleTile, mercator_view::TexturedVisibleTile>
  174. for TileAtlas {
  175. fn textured_visible_tiles<'b>(
  176. &mut self,
  177. cx: &mut Context,
  178. visible_tiles: &'b [mercator_view::VisibleTile],
  179. max_tiles_to_use: usize,
  180. source: &TileSource,
  181. cache: &mut TileCache,
  182. ) -> (Vec<mercator_view::TexturedVisibleTile>, Option<&'b [mercator_view::VisibleTile]>, usize)
  183. {
  184. let mut tvt = Vec::with_capacity(visible_tiles.len());
  185. let (inset_x, inset_y) = self.texture_margins();
  186. let num_usable_slots = self.slots_lru.len();
  187. // The number of actually used slots may be lower, because slots can be used multiple times
  188. // in the same view (especially the default slot).
  189. let mut used_slots = 0_usize;
  190. for (i, vt) in visible_tiles.iter().enumerate() {
  191. if used_slots >= num_usable_slots || used_slots >= max_tiles_to_use {
  192. return (tvt, Some(&visible_tiles[i..]), used_slots);
  193. }
  194. if let Some(slot) = self.store(cx, vt.tile, source, cache, true) {
  195. let tex_rect = self.slot_to_texture_rect(slot);
  196. used_slots += 1;
  197. tvt.push(
  198. mercator_view::TexturedVisibleTile {
  199. screen_rect: vt.rect,
  200. tex_rect,
  201. tex_minmax: tex_rect.inset(inset_x, inset_y),
  202. }
  203. );
  204. } else {
  205. // exact tile not found
  206. if used_slots + 5 > num_usable_slots || used_slots + 5 > max_tiles_to_use {
  207. return (tvt, Some(&visible_tiles[i..]), used_slots);
  208. }
  209. // default tile
  210. let mut tex_sub_rect = self.slot_to_texture_rect(Self::default_slot());
  211. let mut tex_rect = tex_sub_rect;
  212. // look for cached tiles in lower zoom layers
  213. for dist in 1..31 {
  214. if let Some((parent_tile, sub_coord)) = vt.tile.parent(dist) {
  215. if let Some(slot) = self.store(cx, parent_tile, source, cache, false) {
  216. used_slots += 1;
  217. tex_sub_rect = self.subslot_to_texture_rect(slot, sub_coord);
  218. tex_rect = self.slot_to_texture_rect(slot);
  219. break;
  220. }
  221. } else {
  222. break;
  223. }
  224. }
  225. // look for cached tiles in higher zoom layers
  226. //TODO Just create one rect (instead of four) if there is no tile from a higher
  227. // zoom level available
  228. for (child_tile, child_sub_coord) in vt.tile.children_iter(1) {
  229. if let Some(slot) = self.store(cx, child_tile, source, cache, false) {
  230. used_slots += 1;
  231. let tex_rect = self.slot_to_texture_rect(slot);
  232. tvt.push(
  233. mercator_view::TexturedVisibleTile {
  234. screen_rect: vt.rect.subdivide(&child_sub_coord),
  235. tex_rect,
  236. tex_minmax: tex_rect.inset(inset_x, inset_y),
  237. }
  238. );
  239. } else {
  240. tvt.push(
  241. mercator_view::TexturedVisibleTile {
  242. screen_rect: vt.rect.subdivide(&child_sub_coord),
  243. tex_rect: tex_sub_rect.subdivide(&child_sub_coord),
  244. tex_minmax: tex_rect.inset(inset_x, inset_y),
  245. }
  246. );
  247. }
  248. }
  249. };
  250. }
  251. (tvt, None, used_slots)
  252. }
  253. }
  254. impl VisibleTilesProvider<orthografic_view::VisibleTile, orthografic_view::TexturedVisibleTile>
  255. for TileAtlas {
  256. fn textured_visible_tiles<'b>(
  257. &mut self,
  258. cx: &mut Context,
  259. visible_tiles: &'b [orthografic_view::VisibleTile],
  260. max_tiles_to_use: usize,
  261. source: &TileSource,
  262. cache: &mut TileCache,
  263. ) -> (Vec<orthografic_view::TexturedVisibleTile>, Option<&'b [orthografic_view::VisibleTile]>,
  264. usize)
  265. {
  266. let mut tvt = Vec::with_capacity(visible_tiles.len());
  267. let (inset_x, inset_y) = self.texture_margins();
  268. let num_usable_slots = self.slots_lru.len();
  269. // The number of actually used slots may be lower, because slots can be used multiple times
  270. // in the same view (especially the default slot).
  271. let mut used_slots = 0_usize;
  272. for (i, vt) in visible_tiles.iter().enumerate() {
  273. if used_slots >= num_usable_slots || used_slots >= max_tiles_to_use {
  274. return (tvt, Some(&visible_tiles[i..]), used_slots);
  275. }
  276. if let Some(slot) = self.store(cx, vt.tile, source, cache, true) {
  277. let tex_rect = self.slot_to_texture_rect(slot);
  278. used_slots += 1;
  279. tvt.push(
  280. orthografic_view::TexturedVisibleTile {
  281. tile_coord: vt.tile,
  282. tex_rect,
  283. tex_minmax: tex_rect.inset(inset_x, inset_y),
  284. }
  285. );
  286. } else {
  287. // exact tile not found
  288. if used_slots + 5 > num_usable_slots || used_slots + 5 > max_tiles_to_use {
  289. return (tvt, Some(&visible_tiles[i..]), used_slots);
  290. }
  291. // default tile
  292. let mut tex_sub_rect = self.slot_to_texture_rect(Self::default_slot());
  293. let mut tex_rect = tex_sub_rect;
  294. // look for cached tiles in lower zoom layers
  295. for dist in 1..31 {
  296. if let Some((parent_tile, sub_coord)) = vt.tile.parent(dist) {
  297. if let Some(slot) = self.store(cx, parent_tile, source, cache, false) {
  298. used_slots += 1;
  299. tex_sub_rect = self.subslot_to_texture_rect(slot, sub_coord);
  300. tex_rect = self.slot_to_texture_rect(slot);
  301. break;
  302. }
  303. } else {
  304. break;
  305. }
  306. }
  307. // look for cached tiles in higher zoom layers
  308. //TODO Just create one rect (instead of four) if there is no tile from a higher
  309. // zoom level available
  310. for (child_tile, child_sub_coord) in vt.tile.children_iter(1) {
  311. if let Some(slot) = self.store(cx, child_tile, source, cache, false) {
  312. used_slots += 1;
  313. let tex_rect = self.slot_to_texture_rect(slot);
  314. tvt.push(
  315. orthografic_view::TexturedVisibleTile {
  316. tile_coord: child_tile,
  317. tex_rect,
  318. tex_minmax: tex_rect.inset(inset_x, inset_y),
  319. }
  320. );
  321. } else {
  322. tvt.push(
  323. orthografic_view::TexturedVisibleTile {
  324. tile_coord: child_tile,
  325. tex_rect: tex_sub_rect.subdivide(&child_sub_coord),
  326. tex_minmax: tex_rect.inset(inset_x, inset_y),
  327. }
  328. );
  329. }
  330. }
  331. };
  332. }
  333. (tvt, None, used_slots)
  334. }
  335. }