| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- use context::Context;
- use coord::{ScreenRect, SubTileCoord, TileCoord, TextureRect};
- use image;
- use linked_hash_map::LinkedHashMap;
- use map_view::VisibleTile;
- use std::collections::HashMap;
- use std::collections::hash_map::Entry;
- use texture::Texture;
- use tile::Tile;
- use tile_cache::TileCache;
- use tile_source::TileSource;
-
- #[derive(Clone, Debug)]
- pub struct TexturedVisibleTile {
- pub screen_rect: ScreenRect,
- pub tex_rect: TextureRect,
- pub tex_minmax: TextureRect,
- }
-
- #[derive(Clone, Debug)]
- pub struct TileAtlas {
- texture: Texture,
- tile_size: u32,
- slots_lru: LinkedHashMap<CacheSlot, Option<Tile>>, // LRU cache of slots
- tile_to_slot: HashMap<Tile, CacheSlot>,
- use_async: bool,
- }
-
- impl TileAtlas {
- fn init(&mut self, cx: &mut Context) {
- // add tile for default slot
- {
- let img = image::load_from_memory(
- include_bytes!("../img/no_tile.png"),
- ).unwrap();
- self.texture.sub_image(cx, 0, 0, &img);
- }
-
- let slots_x = self.texture.width() / self.tile_size;
- let slots_y = self.texture.height() / self.tile_size;
- let num_slots = (slots_x * slots_y) as usize;
-
- self.slots_lru.clear();
- self.slots_lru.reserve(num_slots);
- for x in 0..slots_x {
- for y in 0..slots_y {
- let slot = CacheSlot { x, y };
- self.slots_lru.insert(slot, None);
- }
- }
- self.slots_lru.remove(&Self::default_slot());
-
- self.tile_to_slot.clear();
- self.tile_to_slot.reserve(num_slots);
- }
-
- pub fn new(cx: &mut Context, tex: Texture, tile_size: u32, use_async: bool) -> Self {
- let mut atlas = TileAtlas {
- texture: tex,
- tile_size,
- slots_lru: LinkedHashMap::new(),
- tile_to_slot: HashMap::new(),
- use_async,
- };
-
- atlas.init(cx);
- atlas
- }
-
- pub fn double_texture_size(&mut self, cx: &mut Context) -> Result<(), ()> {
- let max_size = cx.max_texture_size() as u32;
-
- let new_width = self.texture.width() * 2;
- let new_height = self.texture.height() * 2;
-
- if new_width <= max_size && new_height <= max_size {
- self.texture.resize(cx, new_width, new_height);
-
- // remove old entries, initialize texture
- self.init(cx);
-
- info!("new atlas size {}x{}", new_width, new_height);
-
- Ok(())
- } else {
- Err(())
- }
- }
-
- pub fn default_slot() -> CacheSlot {
- CacheSlot { x: 0, y: 0 }
- }
-
- pub fn store(&mut self, cx: &mut Context, tile_coord: TileCoord, source: &TileSource, cache: &mut TileCache, load: bool) -> Option<CacheSlot> {
- let mut remove_tile = None;
- let tile = Tile::new(tile_coord, source.id());
-
- let slot = match self.tile_to_slot.entry(tile) {
- Entry::Vacant(entry) => {
- let img_option = if load {
- if self.use_async {
- cache.get_async(tile_coord, source, true)
- } else {
- cache.get_sync(tile_coord, source, true)
- }
- } else {
- cache.lookup(tile)
- };
-
- if let Some(img) = img_option {
- let (slot, old_tile) = self.slots_lru.pop_front().unwrap();
- self.slots_lru.insert(slot, Some(tile));
-
- remove_tile = old_tile;
-
- self.texture.sub_image(
- cx,
- (slot.x * self.tile_size) as i32,
- (slot.y * self.tile_size) as i32,
- img,
- );
- Some(*entry.insert(slot))
- } else {
- None
- }
- },
- Entry::Occupied(entry) => {
- let slot = *entry.into_mut();
-
- self.slots_lru.get_refresh(&slot);
-
- Some(slot)
- },
- };
-
- if let Some(t) = remove_tile {
- self.tile_to_slot.remove(&t);
- }
-
- slot
- }
-
- /// Finds textures from the cache for a given slice of visible tiles. The texture atlas may not
- /// be big enough to hold all textures at once; a possible remainder of untextured visible tiles is
- /// returned as an `Option`.
- /// The function guarantees that no more than `max_tiles_to_use` tiles are used for texturing;
- /// the number of used tiles is returned as an `usize`.
- pub fn textured_visible_tiles<'b>(
- &mut self,
- cx: &mut Context,
- visible_tiles: &'b [VisibleTile],
- max_tiles_to_use: usize,
- source: &TileSource,
- cache: &mut TileCache,
- ) -> (Vec<TexturedVisibleTile>, Option<&'b [VisibleTile]>, usize)
- {
- let mut tvt = Vec::with_capacity(visible_tiles.len());
-
- let inset_x = 0.5 / f64::from(self.texture.width());
- let inset_y = 0.5 / f64::from(self.texture.height());
-
- let num_usable_slots = self.slots_lru.len();
- // The number of actually used slots may be lower, because slots can be used multiple times
- // in the same view (especially the default slot).
- let mut used_slots = 0_usize;
-
- for (i, vt) in visible_tiles.iter().enumerate() {
- if used_slots >= num_usable_slots || used_slots >= max_tiles_to_use {
- return (tvt, Some(&visible_tiles[i..]), used_slots);
- }
-
- if let Some(slot) = self.store(cx, vt.tile, source, cache, true) {
- let tex_rect = self.slot_to_texture_rect(slot);
- used_slots += 1;
- tvt.push(
- TexturedVisibleTile {
- screen_rect: vt.rect,
- tex_rect,
- tex_minmax: tex_rect.inset(inset_x, inset_y),
- }
- );
- } else {
- // exact tile not found
-
- if used_slots + 5 > num_usable_slots || used_slots + 5 > max_tiles_to_use {
- return (tvt, Some(&visible_tiles[i..]), used_slots);
- }
-
- // default tile
- let mut tex_sub_rect = self.slot_to_texture_rect(Self::default_slot());
- let mut tex_rect = tex_sub_rect;
-
- // look for cached tiles in lower zoom layers
- for dist in 1..31 {
- if let Some((parent_tile, sub_coord)) = vt.tile.parent(dist) {
- if let Some(slot) = self.store(cx, parent_tile, source, cache, false) {
- used_slots += 1;
- tex_sub_rect = self.subslot_to_texture_rect(slot, sub_coord);
- tex_rect = self.slot_to_texture_rect(slot);
- break;
- }
- } else {
- break;
- }
- }
-
- // look for cached tiles in higher zoom layers
- for &(child_tile, child_sub_coord) in &vt.tile.children() {
- if let Some(slot) = self.store(cx, child_tile, source, cache, false) {
- used_slots += 1;
- let tex_rect = self.slot_to_texture_rect(slot);
-
- tvt.push(
- TexturedVisibleTile {
- screen_rect: vt.rect.subdivide(&child_sub_coord),
- tex_rect,
- tex_minmax: tex_rect.inset(inset_x, inset_y),
- }
- );
- } else {
- tvt.push(
- TexturedVisibleTile {
- screen_rect: vt.rect.subdivide(&child_sub_coord),
- tex_rect: tex_sub_rect.subdivide(&child_sub_coord),
- tex_minmax: tex_rect.inset(inset_x, inset_y),
- }
- );
- }
- }
- };
- }
-
- (tvt, None, used_slots)
- }
-
- fn slot_to_texture_rect(&self, slot: CacheSlot) -> TextureRect {
- let scale_x = f64::from(self.tile_size) / f64::from(self.texture.width());
- let scale_y = f64::from(self.tile_size) / f64::from(self.texture.height());
-
- TextureRect {
- x1: f64::from(slot.x) * scale_x,
- y1: f64::from(slot.y) * scale_y,
- x2: f64::from(slot.x + 1) * scale_x,
- y2: f64::from(slot.y + 1) * scale_y,
- }
- }
-
- fn subslot_to_texture_rect(&self, slot: CacheSlot, sub_coord: SubTileCoord) -> TextureRect {
- let scale_x = f64::from(self.tile_size) / (f64::from(self.texture.width()) * f64::from(sub_coord.size));
- let scale_y = f64::from(self.tile_size) / (f64::from(self.texture.height()) * f64::from(sub_coord.size));
-
- TextureRect {
- x1: f64::from(slot.x * sub_coord.size + sub_coord.x) * scale_x,
- y1: f64::from(slot.y * sub_coord.size + sub_coord.y) * scale_y,
- x2: f64::from(slot.x * sub_coord.size + sub_coord.x + 1) * scale_x,
- y2: f64::from(slot.y * sub_coord.size + sub_coord.y + 1) * scale_y,
- }
- }
-
- pub fn texture(&self) -> &Texture {
- &self.texture
- }
- }
-
- #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
- pub struct CacheSlot {
- pub x: u32,
- pub y: u32,
- }
|