diff --git a/MapControl/Shared/ImageLoader.cs b/MapControl/Shared/ImageLoader.cs index 4dccbb31..401fd4b7 100644 --- a/MapControl/Shared/ImageLoader.cs +++ b/MapControl/Shared/ImageLoader.cs @@ -24,7 +24,7 @@ namespace MapControl /// public static HttpClient HttpClient { get; set; } = new HttpClient(); - public static async Task LoadImageAsync(Uri uri, bool isTileImage) + public static async Task LoadImageAsync(Uri uri) { ImageSource imageSource = null; @@ -34,7 +34,7 @@ namespace MapControl } else if (uri.Scheme == "http") { - imageSource = await LoadHttpImageAsync(uri, isTileImage); + imageSource = await LoadHttpImageAsync(uri); } else { @@ -44,7 +44,7 @@ namespace MapControl return imageSource; } - public static async Task LoadHttpImageAsync(Uri uri, bool isTileImage) + public static async Task LoadHttpImageAsync(Uri uri) { ImageSource imageSource = null; @@ -54,12 +54,9 @@ namespace MapControl { Debug.WriteLine("ImageLoader: {0}: {1} {2}", uri, (int)response.StatusCode, response.ReasonPhrase); } - else if (!isTileImage || IsTileAvailable(response.Headers)) + else if (IsTileAvailable(response.Headers)) { - using (var stream = await GetResponseStreamAsync(response.Content)) - { - imageSource = await CreateImageSourceAsync(stream); - } + imageSource = await CreateImageSourceAsync(response.Content); } return imageSource; diff --git a/MapControl/Shared/TileSource.cs b/MapControl/Shared/TileSource.cs index b516105b..a5b73faf 100644 --- a/MapControl/Shared/TileSource.cs +++ b/MapControl/Shared/TileSource.cs @@ -119,7 +119,7 @@ namespace MapControl { try { - imageSource = await ImageLoader.LoadImageAsync(uri, true); + imageSource = await ImageLoader.LoadImageAsync(uri); } catch (Exception ex) { diff --git a/MapControl/Shared/WmsImageLayer.cs b/MapControl/Shared/WmsImageLayer.cs index c6bab315..46a82c0e 100644 --- a/MapControl/Shared/WmsImageLayer.cs +++ b/MapControl/Shared/WmsImageLayer.cs @@ -126,7 +126,7 @@ namespace MapControl try { - imageSource = await ImageLoader.LoadImageAsync(new Uri(uri.Replace(" ", "%20")), false); + imageSource = await ImageLoader.LoadImageAsync(new Uri(uri.Replace(" ", "%20"))); } catch (Exception ex) { diff --git a/MapControl/UWP/ImageLoader.UWP.cs b/MapControl/UWP/ImageLoader.UWP.cs index bd563dec..8d297540 100644 --- a/MapControl/UWP/ImageLoader.UWP.cs +++ b/MapControl/UWP/ImageLoader.UWP.cs @@ -35,23 +35,39 @@ namespace MapControl return imageSource; } - public static async Task LoadHttpTileImageAsync(Uri uri, Func tileCallback) + public static async Task> LoadHttpBufferAsync(Uri uri) { - using (var response = await HttpClient.GetAsync(uri)) + Tuple result = null; + + try { - if (!response.IsSuccessStatusCode) + using (var response = await HttpClient.GetAsync(uri)) { - Debug.WriteLine("ImageLoader: {0}: {1} {2}", uri, (int)response.StatusCode, response.ReasonPhrase); - } - else if (IsTileAvailable(response.Headers)) - { - var buffer = await response.Content.ReadAsBufferAsync(); + if (!response.IsSuccessStatusCode) + { + Debug.WriteLine("ImageLoader: {0}: {1} {2}", uri, (int)response.StatusCode, response.ReasonPhrase); + } + else + { + IBuffer buffer = null; + TimeSpan? maxAge = null; - await tileCallback(buffer, response.Headers.CacheControl?.MaxAge); - } + if (IsTileAvailable(response.Headers)) + { + buffer = await response.Content.ReadAsBufferAsync(); + maxAge = response.Headers.CacheControl?.MaxAge; + } - return response.IsSuccessStatusCode; + result = new Tuple(buffer, maxAge); + } + } } + catch (Exception ex) + { + Debug.WriteLine("ImageLoader: {0}: {1}", uri, ex.Message); + } + + return result; } public static async Task CreateImageSourceAsync(IRandomAccessStream stream) @@ -61,12 +77,14 @@ namespace MapControl return bitmapImage; } - private static async Task GetResponseStreamAsync(IHttpContent content) + private static async Task CreateImageSourceAsync(IHttpContent content) { - var stream = new InMemoryRandomAccessStream(); - await content.WriteToStreamAsync(stream); - stream.Seek(0); - return stream; + using (var stream = new InMemoryRandomAccessStream()) + { + await content.WriteToStreamAsync(stream); + stream.Seek(0); + return await CreateImageSourceAsync(stream); + } } private static bool IsTileAvailable(HttpResponseHeaderCollection responseHeaders) diff --git a/MapControl/UWP/Tile.UWP.cs b/MapControl/UWP/Tile.UWP.cs index 058f65f2..e0b2aeb4 100644 --- a/MapControl/UWP/Tile.UWP.cs +++ b/MapControl/UWP/Tile.UWP.cs @@ -21,7 +21,7 @@ namespace MapControl { var bitmapImage = imageSource as BitmapImage; - if (bitmapImage != null && bitmapImage.UriSource != null) + if (bitmapImage?.UriSource != null) { bitmapImage.ImageOpened += BitmapImageOpened; bitmapImage.ImageFailed += BitmapImageFailed; diff --git a/MapControl/UWP/TileImageLoader.UWP.cs b/MapControl/UWP/TileImageLoader.UWP.cs index e2136a96..7d2048be 100644 --- a/MapControl/UWP/TileImageLoader.UWP.cs +++ b/MapControl/UWP/TileImageLoader.UWP.cs @@ -3,7 +3,6 @@ // Licensed under the Microsoft Public License (Ms-PL) using System; -using System.Diagnostics; using System.Threading.Tasks; using Windows.Storage; using Windows.Storage.Streams; @@ -36,26 +35,24 @@ namespace MapControl { var cacheItem = await Cache.GetAsync(cacheKey); var cacheBuffer = cacheItem?.Buffer; - var loaded = false; if (cacheBuffer == null || cacheItem.Expiration < DateTime.UtcNow) { - try + var result = await ImageLoader.LoadHttpBufferAsync(uri); + + if (result != null) // download succeeded { - loaded = await ImageLoader.LoadHttpTileImageAsync(uri, - async (buffer, maxAge) => - { - await SetTileImageAsync(tile, buffer); // create BitmapImage before caching - await Cache.SetAsync(cacheKey, buffer, GetExpiration(maxAge)); - }); - } - catch (Exception ex) - { - Debug.WriteLine("TileImageLoader: {0}: {1}", uri, ex.Message); + cacheBuffer = null; // discard cached image + + if (result.Item1 != null) // tile image available + { + await SetTileImageAsync(tile, result.Item1); // show before caching + await Cache.SetAsync(cacheKey, result.Item1, GetExpiration(result.Item2)); + } } } - if (!loaded && cacheBuffer != null) // keep expired image if download failed + if (cacheBuffer != null) { await SetTileImageAsync(tile, cacheBuffer); } diff --git a/MapControl/WPF/ImageLoader.WPF.cs b/MapControl/WPF/ImageLoader.WPF.cs index cceee79c..fda4aa73 100644 --- a/MapControl/WPF/ImageLoader.WPF.cs +++ b/MapControl/WPF/ImageLoader.WPF.cs @@ -36,27 +36,41 @@ namespace MapControl }); } - public static async Task LoadHttpTileImageAsync(Uri uri, Func tileCallback) + public static async Task> LoadHttpStreamAsync(Uri uri) { - using (var response = await HttpClient.GetAsync(uri)) - { - if (!response.IsSuccessStatusCode) - { - Debug.WriteLine("ImageLoader: {0}: {1} {2}", uri, (int)response.StatusCode, response.ReasonPhrase); - } - else if (IsTileAvailable(response.Headers)) - { - using (var stream = new MemoryStream()) - { - await response.Content.CopyToAsync(stream); - stream.Seek(0, SeekOrigin.Begin); + Tuple result = null; - await tileCallback(stream, response.Headers.CacheControl?.MaxAge); + try + { + using (var response = await HttpClient.GetAsync(uri)) + { + if (!response.IsSuccessStatusCode) + { + Debug.WriteLine("ImageLoader: {0}: {1} {2}", uri, (int)response.StatusCode, response.ReasonPhrase); + } + else + { + MemoryStream stream = null; + TimeSpan? maxAge = null; + + if (IsTileAvailable(response.Headers)) + { + stream = new MemoryStream(); + await response.Content.CopyToAsync(stream); + stream.Seek(0, SeekOrigin.Begin); + maxAge = response.Headers.CacheControl?.MaxAge; + } + + result = new Tuple(stream, maxAge); } } - - return response.IsSuccessStatusCode; } + catch (Exception ex) + { + Debug.WriteLine("ImageLoader: {0}: {1}", uri, ex.Message); + } + + return result; } public static ImageSource CreateImageSource(Stream stream) @@ -75,12 +89,14 @@ namespace MapControl return Task.Run(() => CreateImageSource(stream)); } - private static async Task GetResponseStreamAsync(HttpContent content) + private static async Task CreateImageSourceAsync(HttpContent content) { - var stream = new MemoryStream(); - await content.CopyToAsync(stream); - stream.Seek(0, SeekOrigin.Begin); - return stream; + using (var stream = new MemoryStream()) + { + await content.CopyToAsync(stream); + stream.Seek(0, SeekOrigin.Begin); + return await CreateImageSourceAsync(stream); + } } private static bool IsTileAvailable(HttpResponseHeaders responseHeaders) diff --git a/MapControl/WPF/TileImageLoader.WPF.cs b/MapControl/WPF/TileImageLoader.WPF.cs index 08fd41db..7e78af72 100644 --- a/MapControl/WPF/TileImageLoader.WPF.cs +++ b/MapControl/WPF/TileImageLoader.WPF.cs @@ -3,7 +3,6 @@ // Licensed under the Microsoft Public License (Ms-PL) using System; -using System.Diagnostics; using System.IO; using System.Net; using System.Runtime.Caching; @@ -36,40 +35,41 @@ namespace MapControl private async Task LoadTileImageAsync(Tile tile, Uri uri, string cacheKey) { DateTime expiration; - var buffer = GetCachedImage(cacheKey, out expiration); - var loaded = false; + var cacheBuffer = GetCachedImage(cacheKey, out expiration); - if (buffer == null || expiration < DateTime.UtcNow) + if (cacheBuffer == null || expiration < DateTime.UtcNow) { - try + var result = await ImageLoader.LoadHttpStreamAsync(uri); + + if (result != null) // download succeeded { - loaded = await ImageLoader.LoadHttpTileImageAsync(uri, - async (stream, maxAge) => + cacheBuffer = null; // discard cached image + + if (result.Item1 != null) // tile image available + { + using (var stream = result.Item1) { - await SetTileImageAsync(tile, stream); // create BitmapImage before caching - SetCachedImage(cacheKey, stream, GetExpiration(maxAge)); - }); - } - catch (Exception ex) - { - Debug.WriteLine("TileImageLoader: {0}: {1}", uri, ex.Message); + SetTileImage(tile, stream); // show before caching + SetCachedImage(cacheKey, stream, GetExpiration(result.Item2)); + } + } } } - if (!loaded && buffer != null) // keep expired image if download failed + if (cacheBuffer != null) { - using (var stream = new MemoryStream(buffer)) + using (var stream = new MemoryStream(cacheBuffer)) { - await SetTileImageAsync(tile, stream); + SetTileImage(tile, stream); } } } - private async Task SetTileImageAsync(Tile tile, MemoryStream stream) + private void SetTileImage(Tile tile, Stream stream) { var imageSource = ImageLoader.CreateImageSource(stream); - await tile.Image.Dispatcher.InvokeAsync(() => tile.SetImage(imageSource)); + tile.Image.Dispatcher.InvokeAsync(() => tile.SetImage(imageSource)); } private static byte[] GetCachedImage(string cacheKey, out DateTime expiration)