public virtual void Put(Job key, ITileBitmap bitmap) { lock (this) { // no-op } }
public virtual void Put(Job key, ITileBitmap bitmap) { lock (this) { if (key == null) { throw new System.ArgumentException("key must not be null"); } else if (bitmap == null) { throw new System.ArgumentException("bitmap must not be null"); } ITileBitmap old = this.lruCache.Get(key); if (old != null) { old.DecrementRefCount(); } if (this.lruCache.Add(key, bitmap) != null) { LOGGER.Warn("overwriting cached entry: " + key); } bitmap.IncrementRefCount(); this.observable.NotifyObservers(); } }
public override void Draw(BoundingBox boundingBox, sbyte zoomLevel, ICanvas canvas, Point topLeftPoint) { IList <TilePosition> tilePositions = LayerUtil.GetTilePositions(boundingBox, zoomLevel, topLeftPoint, this.displayModel.TileSize); // In a rotation situation it is possible that drawParentTileBitmap sets the // clipping bounds to portrait, while the device is just being rotated into // landscape: the result is a partially painted screen that only goes away // after zooming (which has the effect of resetting the clip bounds if drawParentTileBitmap // is called again). // Always resetting the clip bounds here seems to avoid the problem, // I assume that this is a pretty cheap operation, otherwise it would be better // to hook this into the onConfigurationChanged call chain. canvas.ResetClip(); if (!isTransparent) { canvas.FillColor(this.displayModel.BackgroundColor); } ISet <Job> jobs = new HashSet <Job>(); foreach (TilePosition tilePosition in tilePositions) { jobs.Add(CreateJob(tilePosition.Tile)); } this.tileCache.WorkingSet = jobs; for (int i = tilePositions.Count - 1; i >= 0; --i) { TilePosition tilePosition = tilePositions[i]; Point point = tilePosition.Point; Tile tile = tilePosition.Tile; T job = CreateJob(tile); ITileBitmap bitmap = this.tileCache.GetImmediately(job); if (bitmap == null) { if (this.hasJobQueue && !this.tileCache.ContainsKey(job)) { this.jobQueue.Add(job); } DrawParentTileBitmap(canvas, point, tile); } else { if (IsTileStale(tile, bitmap) && this.hasJobQueue && !this.tileCache.ContainsKey(job)) { this.jobQueue.Add(job); } RetrieveLabelsOnly(job); canvas.DrawBitmap(bitmap, (int)Math.Round(point.X), (int)Math.Round(point.Y)); bitmap.DecrementRefCount(); } } if (this.hasJobQueue) { this.jobQueue.NotifyWorkers(); } }
/// <summary> /// Whether the tile is stale and should be refreshed. /// <para> /// This method is called from <seealso cref="#draw(BoundingBox, byte, Canvas, Point)"/> to determine whether the tile needs to /// be refreshed. /// </para> /// <para> /// A tile is considered stale if one or more of the following two conditions apply: /// <ul> /// <li>The {@code bitmap}'s <seealso cref="MapsforgeSharp.Core.Graphics.ITileBitmap#isExpired()"/> method returns {@code True}.</li> /// <li>The layer has a time-to-live (TTL) set (<seealso cref="#getCacheTimeToLive()"/> returns a nonzero value) and the sum of /// the {@code bitmap}'s <seealso cref="MapsforgeSharp.Core.Graphics.ITileBitmap#getTimestamp()"/> and TTL is less than current /// time (as returned by <seealso cref="java.lang.System#currentTimeMillis()"/>).</li> /// </ul> /// </para> /// <para> /// When a tile has become stale, the layer will first display the tile referenced by {@code bitmap} and attempt to /// obtain a fresh copy in the background. When a fresh copy becomes available, the layer will replace it and update /// the cache. If a fresh copy cannot be obtained (e.g. because the tile is obtained from an online source which /// cannot be reached), the stale tile will continue to be used until another /// {@code #draw(BoundingBox, byte, Canvas, Point)} operation requests it again. /// /// </para> /// </summary> /// <param name="tile"> /// A tile. This parameter is not used for a {@code TileDownloadLayer} and can be null. </param> /// <param name="bitmap"> /// The bitmap for {@code tile} currently held in the layer's cache. </param> protected internal override bool IsTileStale(Tile tile, ITileBitmap bitmap) { if (bitmap.Expired) { return(true); } return(cacheTimeToLive != 0 && ((bitmap.Timestamp + cacheTimeToLive) < DateTimeHelperClass.CurrentUnixTimeMillis())); }
public virtual void Put(Job key, ITileBitmap bitmap) { if (this.workingSet.Contains(key)) { this.firstLevelTileCache.Put(key, bitmap); } this.secondLevelTileCache.Put(key, bitmap); }
public override void run() { ITileBitmap bitmap = null; try { long start = 0; if (outerInstance.inShutdown) { return; } if (DEBUG_TIMING) { start = DateTimeHelperClass.CurrentUnixTimeMillis(); LOGGER.Info("ConcurrentJobs " + Interlocked.Increment(ref outerInstance.concurrentJobs)); } bitmap = outerInstance.databaseRenderer.ExecuteJob(rendererJob); if (outerInstance.inShutdown) { return; } if (!rendererJob.labelsOnly && bitmap != null) { outerInstance.tileCache.Put(rendererJob, bitmap); outerInstance.databaseRenderer.RemoveTileInProgress(rendererJob.tile); } outerInstance.layer.RequestRedraw(); if (DEBUG_TIMING) { long end = DateTimeHelperClass.CurrentUnixTimeMillis(); long te = Interlocked.Increment(ref outerInstance.totalExecutions); long tt = Interlocked.Add(ref outerInstance.totalTime, end - start); if (te % 10 == 0) { LOGGER.Info("TIMING " + Convert.ToString(te) + " " + Convert.ToString(tt / te)); } Interlocked.Decrement(ref outerInstance.concurrentJobs); } } finally { this.rendererJob.renderThemeFuture.DecrementRefCount(); outerInstance.jobQueue.Remove(rendererJob); if (bitmap != null) { bitmap.DecrementRefCount(); } } }
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET: //ORIGINAL LINE: private void downloadTile(DownloadJob downloadJob) throws java.io.IOException private void downloadTile(DownloadJob downloadJob) { TileDownloader tileDownloader = new TileDownloader(downloadJob, this.graphicFactory); ITileBitmap bitmap = tileDownloader.DownloadImage(); if (!Interrupted && bitmap != null) { bitmap.ScaleTo(this.displayModel.TileSize, this.displayModel.TileSize); this.tileCache.Put(downloadJob, bitmap); this.layer.RequestRedraw(); } }
public virtual ITileBitmap Get(Job key) { lock (this) { ITileBitmap bitmap = this.lruCache.Get(key); if (bitmap != null) { bitmap.IncrementRefCount(); } return(bitmap); } }
/// <summary> /// Draws a bitmap just with outside colour, used for bitmaps outside of map area. </summary> /// <param name="renderContext"> the RenderContext </param> /// <returns> bitmap drawn in single colour. </returns> private ITileBitmap CreateBackgroundBitmap(RenderContext renderContext) { ITileBitmap bitmap = this.graphicFactory.CreateTileBitmap(renderContext.rendererJob.tile.TileSize, renderContext.rendererJob.hasAlpha); renderContext.canvasRasterer.CanvasBitmap = bitmap; if (!renderContext.rendererJob.hasAlpha) { renderContext.canvasRasterer.Fill(renderContext.renderTheme.MapBackgroundOutside); } return(bitmap); }
/// <summary> /// stores the bitmap data on disk with filename key /// </summary> /// <param name="key"> /// filename </param> /// <param name="bitmap"> /// tile image </param> private void StoreData(Job key, ITileBitmap bitmap) { try { IFile file = GetOutputFile(key); if (file == null) { // if the file cannot be written, silently return return; } using (System.IO.Stream outputStream = file.OpenAsync(FileAccess.ReadAndWrite).Result) { bitmap.Compress(outputStream); try { //@lock.writeLock().@lock(); if (this.lruCache.Add(key.Key, file) != null) { LOGGER.Warn("overwriting cached entry: " + key.Key); } } finally { //@lock.writeLock().unlock(); } } } catch (Exception e) { // we are catching now any exception and then disable the file cache // this should ensure that no exception in the storage thread will // ever crash the main app. If there is a runtime exception, the thread // will exit (via destroy). LOGGER.Fatal("Disabling filesystem cache", e); // most likely cause is that the disk is full, just disable the // cache otherwise // more and more exceptions will be thrown. this.Destroy(); try { //@lock.writeLock().@lock(); this.lruCache = new FileWorkingSetCache <string>(0); } finally { //@lock.writeLock().unlock(); } } }
public virtual ITileBitmap Get(Job key) { ITileBitmap returnBitmap = this.firstLevelTileCache.Get(key); if (returnBitmap != null) { return(returnBitmap); } returnBitmap = this.secondLevelTileCache.Get(key); if (returnBitmap != null) { this.firstLevelTileCache.Put(key, returnBitmap); return(returnBitmap); } return(null); }
public virtual ITileBitmap Get(Job key) { IFile file; try { //@lock.readLock().@lock(); file = this.lruCache.Get(key.Key); } finally { //@lock.readLock().unlock(); } if (file == null) { return(null); } try { using (System.IO.Stream inputStream = file.OpenAsync(FileAccess.Read).Result) { ITileBitmap result = this.graphicFactory.CreateTileBitmap(inputStream, key.tile.TileSize, key.hasAlpha); // TODO //result.Timestamp = file.lastModified(); return(result); } } catch (CorruptedInputStreamException e) { // this can happen, at least on Android, when the input stream // is somehow corrupted, returning null ensures it will be loaded // from another source Remove(key); LOGGER.Warn("input stream from file system cache invalid " + key.Key, e); return(null); } catch (IOException e) { Remove(key); LOGGER.Fatal(e.Message, e); return(null); } }
public virtual void Put(Job key, ITileBitmap bitmap) { if (key == null) { throw new System.ArgumentException("key must not be null"); } else if (bitmap == null) { throw new System.ArgumentException("bitmap must not be null"); } if (Capacity == 0) { return; } StoreData(key, bitmap); this.observable.NotifyObservers(); }
internal virtual ITileBitmap DownloadImage() { Uri url = this.downloadJob.tileSource.GetTileUrl(this.downloadJob.tile); using (var httpClient = new System.Net.Http.HttpClient()) { httpClient.Timeout = new TimeSpan(50000); using (var request = new HttpRequestMessage(HttpMethod.Get, url)) { var response = httpClient.SendAsync(request).Result; if (response.IsSuccessStatusCode) { Stream stream; if (response.Headers.TransferEncoding.Contains(new System.Net.Http.Headers.TransferCodingHeaderValue("gzip"))) { stream = new GZipStream(response.Content.ReadAsStreamAsync().Result, CompressionMode.Decompress); } else { stream = response.Content.ReadAsStreamAsync().Result; } try { ITileBitmap result = this.graphicFactory.CreateTileBitmap(stream, this.downloadJob.tile.TileSize, this.downloadJob.hasAlpha); result.Expiration = response.Headers..Content..httpClient.Expiration; return(result); } catch (CorruptedInputStreamException) { // the creation of the tile bit map can fail at, at least on Android, // when the connection is slow or busy, returning null here ensures that // the tile will be downloaded again return(null); } } } } return(null); }
/// <summary> /// Called when a job needs to be executed. /// </summary> /// <param name="rendererJob"> /// the job that should be executed. </param> public virtual ITileBitmap ExecuteJob(RendererJob rendererJob) { RenderTheme renderTheme; try { // Wait until RenderTheme is ready renderTheme = rendererJob.renderThemeFuture.Result; } catch (Exception e) { LOGGER.Fatal("Error to retrieve render theme from future", e); return(null); } RenderContext renderContext = null; try { renderContext = new RenderContext(renderTheme, rendererJob, new CanvasRasterer(graphicFactory)); if (RenderBitmap(renderContext)) { ITileBitmap bitmap = null; if (this.mapDatabase != null) { MapReadResult mapReadResult = this.mapDatabase.ReadMapData(rendererJob.tile); ProcessReadMapData(renderContext, mapReadResult); } if (!rendererJob.labelsOnly) { bitmap = this.graphicFactory.CreateTileBitmap(renderContext.rendererJob.tile.TileSize, renderContext.rendererJob.hasAlpha); bitmap.Timestamp = rendererJob.mapDataStore.GetDataTimestamp(renderContext.rendererJob.tile); renderContext.canvasRasterer.CanvasBitmap = bitmap; if (!rendererJob.hasAlpha && rendererJob.displayModel.BackgroundColor != renderContext.renderTheme.MapBackground) { renderContext.canvasRasterer.Fill(renderContext.renderTheme.MapBackground); } renderContext.canvasRasterer.DrawWays(renderContext); } if (renderLabels) { ISet <MapElementContainer> labelsToDraw = ProcessLabels(renderContext); // now draw the ways and the labels renderContext.canvasRasterer.DrawMapElements(labelsToDraw, renderContext.rendererJob.tile); } else { // store elements for this tile in the label cache this.labelStore.StoreMapItems(renderContext.rendererJob.tile, renderContext.labels); } if (!rendererJob.labelsOnly && renderContext.renderTheme.HasMapBackgroundOutside()) { // blank out all areas outside of map Rectangle insideArea = this.mapDatabase.BoundingBox.GetPositionRelativeToTile(renderContext.rendererJob.tile); if (!rendererJob.hasAlpha) { renderContext.canvasRasterer.FillOutsideAreas(renderContext.renderTheme.MapBackgroundOutside, insideArea); } else { renderContext.canvasRasterer.FillOutsideAreas(Color.TRANSPARENT, insideArea); } } return(bitmap); } // outside of map area with background defined: return(CreateBackgroundBitmap(renderContext)); } finally { if (renderContext != null) { renderContext.Destroy(); } } }
/// <summary> /// Whether the tile is stale and should be refreshed. /// <para> /// This method is not needed for a TileStoreLayer and will always return {@code false}. Both arguments can be null. /// /// </para> /// </summary> /// <param name="tile"> /// A tile. </param> /// <param name="bitmap"> /// The bitmap for {@code tile} currently held in the layer's cache. </param> protected internal override bool IsTileStale(Tile tile, ITileBitmap bitmap) { return(false); }
/// <summary> /// Whether the tile is stale and should be refreshed. /// <para> /// This method is called from <seealso cref="#draw(BoundingBox, byte, Canvas, Point)"/> to determine whether the tile needs to /// be refreshed. Subclasses must override this method and implement appropriate checks to determine when a tile is /// stale. /// </para> /// <para> /// Return {@code false} to use the cached copy without attempting to refresh it. /// </para> /// <para> /// Return {@code true} to cause the layer to attempt to obtain a fresh copy of the tile. The layer will first /// display the tile referenced by {@code bitmap} and attempt to obtain a fresh copy in the background. When a fresh /// copy becomes available, the layer will replace is and update the cache. If a fresh copy cannot be obtained (e.g. /// because the tile is obtained from an online source which cannot be reached), the stale tile will continue to be /// used until another {@code #draw(BoundingBox, byte, Canvas, Point)} operation requests it again. /// /// </para> /// </summary> /// <param name="tile"> /// A tile. </param> /// <param name="bitmap"> /// The bitmap for {@code tile} currently held in the layer's cache. </param> protected internal abstract bool IsTileStale(Tile tile, ITileBitmap bitmap);
/// <summary> /// Whether the tile is stale and should be refreshed. /// <para> /// This method is called from <seealso cref="#draw(MapsforgeSharp.Core.Model.BoundingBox, byte, MapsforgeSharp.Core.Graphics.Canvas, MapsforgeSharp.Core.Model.Point)"/> to determine whether the tile needs to /// be refreshed. /// </para> /// <para> /// A tile is considered stale if the timestamp of the layer's <seealso cref="#mapDataStore"/> is more recent than the /// {@code bitmap}'s <seealso cref="MapsforgeSharp.Core.Graphics.ITileBitmap#getTimestamp()"/>. /// </para> /// <para> /// When a tile has become stale, the layer will first display the tile referenced by {@code bitmap} and attempt to /// obtain a fresh copy in the background. When a fresh copy becomes available, the layer will replace is and update /// the cache. If a fresh copy cannot be obtained for whatever reason, the stale tile will continue to be used until /// another {@code #draw(BoundingBox, byte, Canvas, Point)} operation requests it again. /// /// </para> /// </summary> /// <param name="tile"> /// A tile. </param> /// <param name="bitmap"> /// The bitmap for {@code tile} currently held in the layer's cache. </param> protected internal override bool IsTileStale(Tile tile, ITileBitmap bitmap) { return(this.mapDataStore.GetDataTimestamp(tile) > bitmap.Timestamp); }