From 26bf0b50057538d1ce3adf6031c735967a2cb15b Mon Sep 17 00:00:00 2001 From: ClemensF Date: Thu, 13 Jun 2019 21:38:01 +0200 Subject: [PATCH] Version 4.12.2 Improved TileImageLoader. --- MapControl/Shared/ImageLoader.cs | 12 ++-- MapControl/Shared/MapTileLayer.cs | 16 +++-- MapControl/Shared/Tile.cs | 5 -- MapControl/Shared/TileImageLoader.cs | 95 +++++++++++++-------------- MapControl/UWP/ImageFileCache.UWP.cs | 2 +- MapControl/UWP/ImageLoader.UWP.cs | 51 ++++++++------ MapControl/UWP/TileImageLoader.UWP.cs | 43 +++++------- MapControl/WPF/ImageFileCache.WPF.cs | 2 +- MapControl/WPF/ImageLoader.WPF.cs | 59 ++++++++++------- MapControl/WPF/TileImageLoader.WPF.cs | 12 ++-- MapImages/Shared/WorldFileImage.cs | 5 +- 11 files changed, 162 insertions(+), 140 deletions(-) diff --git a/MapControl/Shared/ImageLoader.cs b/MapControl/Shared/ImageLoader.cs index 8f366f3a..c88d09ee 100644 --- a/MapControl/Shared/ImageLoader.cs +++ b/MapControl/Shared/ImageLoader.cs @@ -7,9 +7,11 @@ using System.Diagnostics; using System.Threading.Tasks; #if WINDOWS_UWP using Windows.Web.Http; +using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Media.Imaging; #else using System.Net.Http; +using System.Windows.Media; using System.Windows.Media.Imaging; #endif @@ -22,9 +24,9 @@ namespace MapControl /// public static HttpClient HttpClient { get; set; } = new HttpClient(); - public static async Task LoadImageAsync(Uri uri) + public static async Task LoadImageAsync(Uri uri) { - BitmapSource image = null; + ImageSource image = null; try { @@ -49,9 +51,9 @@ namespace MapControl return image; } - private static async Task LoadHttpImageAsync(Uri uri) + private static async Task LoadHttpImageAsync(Uri uri) { - BitmapSource image = null; + ImageSource image = null; using (var response = await HttpClient.GetAsync(uri)) { @@ -59,7 +61,7 @@ namespace MapControl { Debug.WriteLine("ImageLoader: {0}: {1} {2}", uri, (int)response.StatusCode, response.ReasonPhrase); } - else if (IsTileAvailable(response.Headers)) + else if (ImageAvailable(response.Headers)) { image = await LoadImageAsync(response.Content); } diff --git a/MapControl/Shared/MapTileLayer.cs b/MapControl/Shared/MapTileLayer.cs index 574d8aa7..2c710dd7 100644 --- a/MapControl/Shared/MapTileLayer.cs +++ b/MapControl/Shared/MapTileLayer.cs @@ -21,7 +21,10 @@ namespace MapControl { public interface ITileImageLoader { - void LoadTilesAsync(IEnumerable tiles, TileSource tileSource, string sourceName); + TileSource TileSource { get; set; } + string SourceName { get; set; } + + void LoadTilesAsync(IEnumerable tiles); } /// @@ -51,7 +54,8 @@ namespace MapControl new PropertyMetadata(null, (o, e) => ((MapTileLayer)o).TileSourcePropertyChanged())); public static readonly DependencyProperty SourceNameProperty = DependencyProperty.Register( - nameof(SourceName), typeof(string), typeof(MapTileLayer), new PropertyMetadata(null)); + nameof(SourceName), typeof(string), typeof(MapTileLayer), + new PropertyMetadata(null, (o, e) => ((MapTileLayer)o).TileImageLoader.SourceName = (string)e.NewValue)); public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register( nameof(Description), typeof(string), typeof(MapTileLayer), new PropertyMetadata(null)); @@ -71,7 +75,7 @@ namespace MapControl new PropertyMetadata(TimeSpan.FromSeconds(0.2), (o, e) => ((MapTileLayer)o).updateTimer.Interval = (TimeSpan)e.NewValue)); public static readonly DependencyProperty UpdateWhileViewportChangingProperty = DependencyProperty.Register( - nameof(UpdateWhileViewportChanging), typeof(bool), typeof(MapTileLayer), new PropertyMetadata(true)); + nameof(UpdateWhileViewportChanging), typeof(bool), typeof(MapTileLayer), new PropertyMetadata(false)); public static readonly DependencyProperty MapBackgroundProperty = DependencyProperty.Register( nameof(MapBackground), typeof(Brush), typeof(MapTileLayer), new PropertyMetadata(null)); @@ -99,7 +103,7 @@ namespace MapControl MapPanel.InitMapElement(this); } - public ITileImageLoader TileImageLoader { get; private set; } + public ITileImageLoader TileImageLoader { get; } public TileGrid TileGrid { get; private set; } @@ -271,6 +275,8 @@ namespace MapControl private void TileSourcePropertyChanged() { + TileImageLoader.TileSource = TileSource; + if (TileGrid != null) { Tiles = new List(); @@ -391,7 +397,7 @@ namespace MapControl Children.Add(tile.Image); } - TileImageLoader.LoadTilesAsync(Tiles, TileSource, SourceName); + TileImageLoader.LoadTilesAsync(Tiles); } } } diff --git a/MapControl/Shared/Tile.cs b/MapControl/Shared/Tile.cs index b691c140..911c3b41 100644 --- a/MapControl/Shared/Tile.cs +++ b/MapControl/Shared/Tile.cs @@ -44,11 +44,6 @@ namespace MapControl } } - public override string ToString() - { - return string.Format("{0}/{1}/{2}", ZoomLevel, XIndex, Y); - } - private void FadeIn() { Image.BeginAnimation(UIElement.OpacityProperty, new DoubleAnimation { From = 0d, To = 1d, Duration = FadeDuration, FillBehavior = FillBehavior.Stop }); diff --git a/MapControl/Shared/TileImageLoader.cs b/MapControl/Shared/TileImageLoader.cs index e26454d0..613e320d 100644 --- a/MapControl/Shared/TileImageLoader.cs +++ b/MapControl/Shared/TileImageLoader.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -38,57 +39,42 @@ namespace MapControl public static TimeSpan DefaultCacheExpiration { get; set; } = TimeSpan.FromDays(1); /// - /// Format string for creating cache keys from the SourceName property of a TileSource, - /// the ZoomLevel, XIndex, and Y properties of a Tile, and the image file extension. - /// The default value is "{0};{1};{2};{3}{4}". + /// Format string for creating cache keys from the SourceName property and the + /// ZoomLevel, XIndex, and Y properties of a Tile and the image file extension. + /// The default value is "{0}/{1}/{2}/{3}{4}". /// - public static string CacheKeyFormat { get; set; } = "{0};{1};{2};{3}{4}"; + public static string CacheKeyFormat { get; set; } = "{0}/{1}/{2}/{3}{4}"; + private readonly TileQueue tileQueue = new TileQueue(); private int taskCount; + public TileSource TileSource { get; set; } + public string SourceName { get; set; } + /// /// Loads all pending tiles from the tiles collection in up to MaxLoadTasks parallel Tasks. - /// If the UriFormat of the TileSource starts with "http" and the sourceName string is non-empty, - /// tile images are cached in the TileImageLoader's Cache. + /// If the UriFormat of TileSource starts with "http" and SourceName is a non-empty string, + /// tile images will be cached in the TileImageLoader's Cache. /// - public void LoadTilesAsync(IEnumerable tiles, TileSource tileSource, string sourceName) + public async void LoadTilesAsync(IEnumerable tiles) { tileQueue.Clear(); + tileQueue.Enqueue(tiles); - if (tileSource != null) + var newTasks = Math.Min(tileQueue.Count, MaxLoadTasks) - taskCount; + + if (newTasks > 0) { - tileQueue.Enqueue(tiles); + Interlocked.Add(ref taskCount, newTasks); - var newTasks = Math.Min(tileQueue.Count, MaxLoadTasks) - taskCount; - - if (newTasks > 0) - { - Func loadTileFunc; - - if (Cache != null && - tileSource.UriFormat != null && - tileSource.UriFormat.StartsWith("http") && - !string.IsNullOrEmpty(sourceName)) - { - loadTileFunc = tile => LoadCachedTileImageAsync(tile, tileSource, sourceName); - } - else - { - loadTileFunc = tile => LoadTileImageAsync(tile, tileSource); - } - - Interlocked.Add(ref taskCount, newTasks); - - while (--newTasks >= 0) - { - Task.Run(() => LoadTilesFromQueueAsync(loadTileFunc)); - } - } + await Task + .WhenAll(Enumerable.Range(0, newTasks).Select(n => LoadTilesFromQueueAsync())) + .ConfigureAwait(false); } } - private async Task LoadTilesFromQueueAsync(Func loadTileFunc) + private async Task LoadTilesFromQueueAsync() { Tile tile; @@ -96,7 +82,7 @@ namespace MapControl { try { - await loadTileFunc(tile); + await LoadTileImageAsync(tile, TileSource, SourceName).ConfigureAwait(false); } catch (Exception ex) { @@ -107,22 +93,35 @@ namespace MapControl Interlocked.Decrement(ref taskCount); } - private async Task LoadCachedTileImageAsync(Tile tile, TileSource tileSource, string sourceName) + private async Task LoadTileImageAsync(Tile tile, TileSource tileSource, string sourceName) { - var uri = tileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel); - - if (uri != null) + if (tileSource != null) { - var extension = Path.GetExtension(uri.LocalPath); - - if (string.IsNullOrEmpty(extension) || extension == ".jpeg") + if (Cache != null && + tileSource.UriFormat != null && + tileSource.UriFormat.StartsWith("http") && + !string.IsNullOrEmpty(sourceName)) { - extension = ".jpg"; + var uri = tileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel); + + if (uri != null) + { + var extension = Path.GetExtension(uri.LocalPath); + + if (string.IsNullOrEmpty(extension) || extension == ".jpeg") + { + extension = ".jpg"; + } + + var cacheKey = string.Format(CacheKeyFormat, sourceName, tile.ZoomLevel, tile.XIndex, tile.Y, extension); + + await LoadCachedTileImageAsync(tile, uri, cacheKey).ConfigureAwait(false); + } + } + else + { + await LoadTileImageAsync(tile, tileSource).ConfigureAwait(false); } - - var cacheKey = string.Format(CacheKeyFormat, sourceName, tile.ZoomLevel, tile.XIndex, tile.Y, extension); - - await LoadCachedTileImageAsync(tile, uri, cacheKey); } } diff --git a/MapControl/UWP/ImageFileCache.UWP.cs b/MapControl/UWP/ImageFileCache.UWP.cs index 3e5db0b2..8c0561d8 100644 --- a/MapControl/UWP/ImageFileCache.UWP.cs +++ b/MapControl/UWP/ImageFileCache.UWP.cs @@ -69,7 +69,7 @@ namespace MapControl.Caching public virtual async Task SetAsync(string key, IBuffer buffer, DateTime expiration) { - var paths = key.Split('\\', '/', ':', ';'); + var paths = key.Split('\\', '/', ',', ':', ';'); try { diff --git a/MapControl/UWP/ImageLoader.UWP.cs b/MapControl/UWP/ImageLoader.UWP.cs index b9d6c094..848ec312 100644 --- a/MapControl/UWP/ImageLoader.UWP.cs +++ b/MapControl/UWP/ImageLoader.UWP.cs @@ -9,6 +9,7 @@ using System.Runtime.InteropServices.WindowsRuntime; using System.Threading.Tasks; using Windows.Storage; using Windows.Storage.Streams; +using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Media.Imaging; using Windows.Web.Http; using Windows.Web.Http.Headers; @@ -17,14 +18,14 @@ namespace MapControl { public static partial class ImageLoader { - public static async Task LoadImageAsync(IRandomAccessStream stream) + public static async Task LoadImageAsync(IRandomAccessStream stream) { var image = new BitmapImage(); await image.SetSourceAsync(stream); return image; } - public static async Task LoadImageAsync(byte[] buffer) + public static async Task LoadImageAsync(byte[] buffer) { using (var stream = new InMemoryRandomAccessStream()) { @@ -34,7 +35,7 @@ namespace MapControl } } - private static async Task LoadImageAsync(IHttpContent content) + private static async Task LoadImageAsync(IHttpContent content) { using (var stream = new InMemoryRandomAccessStream()) { @@ -44,9 +45,9 @@ namespace MapControl } } - private static async Task LoadLocalImageAsync(Uri uri) + private static async Task LoadLocalImageAsync(Uri uri) { - BitmapSource image = null; + ImageSource image = null; var path = uri.IsAbsoluteUri ? uri.LocalPath : uri.OriginalString; if (File.Exists(path)) @@ -62,30 +63,42 @@ namespace MapControl return image; } - internal static async Task> LoadHttpBufferAsync(Uri uri) + internal class HttpBufferResponse { - Tuple result = null; + public readonly IBuffer Buffer; + public readonly TimeSpan? MaxAge; + + public HttpBufferResponse(IBuffer buffer, TimeSpan? maxAge) + { + Buffer = buffer; + MaxAge = maxAge; + } + } + + internal static async Task LoadHttpBufferAsync(Uri uri) + { + HttpBufferResponse response = null; try { - using (var response = await HttpClient.GetAsync(uri)) + using (var responseMessage = await HttpClient.GetAsync(uri)) { - if (!response.IsSuccessStatusCode) - { - Debug.WriteLine("ImageLoader: {0}: {1} {2}", uri, (int)response.StatusCode, response.ReasonPhrase); - } - else + if (responseMessage.IsSuccessStatusCode) { IBuffer buffer = null; TimeSpan? maxAge = null; - if (IsTileAvailable(response.Headers)) + if (ImageAvailable(responseMessage.Headers)) { - buffer = await response.Content.ReadAsBufferAsync(); - maxAge = response.Headers.CacheControl?.MaxAge; + buffer = await responseMessage.Content.ReadAsBufferAsync(); + maxAge = responseMessage.Headers.CacheControl?.MaxAge; } - result = new Tuple(buffer, maxAge); + response = new HttpBufferResponse(buffer, maxAge); + } + else + { + Debug.WriteLine("ImageLoader: {0}: {1} {2}", uri, (int)responseMessage.StatusCode, responseMessage.ReasonPhrase); } } } @@ -94,10 +107,10 @@ namespace MapControl Debug.WriteLine("ImageLoader: {0}: {1}", uri, ex.Message); } - return result; + return response; } - private static bool IsTileAvailable(HttpResponseHeaderCollection responseHeaders) + private static bool ImageAvailable(HttpResponseHeaderCollection responseHeaders) { return !responseHeaders.TryGetValue("X-VE-Tile-Info", out string tileInfo) || tileInfo != "no-tile"; } diff --git a/MapControl/UWP/TileImageLoader.UWP.cs b/MapControl/UWP/TileImageLoader.UWP.cs index fbeed8e2..b53e6694 100644 --- a/MapControl/UWP/TileImageLoader.UWP.cs +++ b/MapControl/UWP/TileImageLoader.UWP.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using Windows.Storage; using Windows.Storage.Streams; using Windows.UI.Core; +using Windows.UI.Xaml.Media; namespace MapControl { @@ -28,58 +29,48 @@ namespace MapControl private async Task LoadCachedTileImageAsync(Tile tile, Uri uri, string cacheKey) { - var cacheItem = await Cache.GetAsync(cacheKey); + var cacheItem = await Cache.GetAsync(cacheKey).ConfigureAwait(false); var cacheBuffer = cacheItem?.Buffer; if (cacheBuffer == null || cacheItem.Expiration < DateTime.UtcNow) { - var result = await ImageLoader.LoadHttpBufferAsync(uri); + var response = await ImageLoader.LoadHttpBufferAsync(uri).ConfigureAwait(false); - if (result != null) // download succeeded + if (response != null) // download succeeded { cacheBuffer = null; // discard cached image - if (result.Item1 != null) // tile image available + if (response.Buffer != null) // tile image available { - await LoadTileImageAsync(tile, result.Item1); - await Cache.SetAsync(cacheKey, result.Item1, GetExpiration(result.Item2)); + await LoadTileImageAsync(tile, response.Buffer).ConfigureAwait(false); + await Cache.SetAsync(cacheKey, response.Buffer, GetExpiration(response.MaxAge)).ConfigureAwait(false); } } } if (cacheBuffer != null) // cached image not expired or download failed { - await LoadTileImageAsync(tile, cacheBuffer); + await LoadTileImageAsync(tile, cacheBuffer).ConfigureAwait(false); } } private async Task LoadTileImageAsync(Tile tile, IBuffer buffer) { - var tcs = new TaskCompletionSource(); - using (var stream = new InMemoryRandomAccessStream()) { await stream.WriteAsync(buffer); stream.Seek(0); - await tile.Image.Dispatcher.RunAsync(CoreDispatcherPriority.Low, async () => - { - try - { - tile.SetImage(await ImageLoader.LoadImageAsync(stream)); - tcs.SetResult(null); - } - catch (Exception ex) - { - tcs.SetException(ex); - } - }); + await SetTileImageAsync(tile, () => ImageLoader.LoadImageAsync(stream)).ConfigureAwait(false); } - - await tcs.Task; } - private async Task LoadTileImageAsync(Tile tile, TileSource tileSource) + private Task LoadTileImageAsync(Tile tile, TileSource tileSource) + { + return SetTileImageAsync(tile, () => tileSource.LoadImageAsync(tile.XIndex, tile.Y, tile.ZoomLevel)); + } + + private async Task SetTileImageAsync(Tile tile, Func> loadImageFunc) { var tcs = new TaskCompletionSource(); @@ -87,7 +78,7 @@ namespace MapControl { try { - tile.SetImage(await tileSource.LoadImageAsync(tile.XIndex, tile.Y, tile.ZoomLevel)); + tile.SetImage(await loadImageFunc()); tcs.SetResult(null); } catch (Exception ex) @@ -96,7 +87,7 @@ namespace MapControl } }); - await tcs.Task; + await tcs.Task.ConfigureAwait(false); } } } diff --git a/MapControl/WPF/ImageFileCache.WPF.cs b/MapControl/WPF/ImageFileCache.WPF.cs index 81ac8af9..61f655b3 100644 --- a/MapControl/WPF/ImageFileCache.WPF.cs +++ b/MapControl/WPF/ImageFileCache.WPF.cs @@ -261,7 +261,7 @@ namespace MapControl.Caching { try { - return Path.Combine(rootFolder, Path.Combine(key.Split('\\', '/', ':', ';'))); + return Path.Combine(rootFolder, Path.Combine(key.Split('\\', '/', ',', ':', ';'))); } catch (Exception ex) { diff --git a/MapControl/WPF/ImageLoader.WPF.cs b/MapControl/WPF/ImageLoader.WPF.cs index 3676d6c5..e9253551 100644 --- a/MapControl/WPF/ImageLoader.WPF.cs +++ b/MapControl/WPF/ImageLoader.WPF.cs @@ -10,13 +10,14 @@ using System.Linq; using System.Net.Http; using System.Net.Http.Headers; using System.Threading.Tasks; +using System.Windows.Media; using System.Windows.Media.Imaging; namespace MapControl { public static partial class ImageLoader { - public static BitmapSource LoadImage(Stream stream) + public static ImageSource LoadImage(Stream stream) { var bitmapImage = new BitmapImage(); bitmapImage.BeginInit(); @@ -27,12 +28,12 @@ namespace MapControl return bitmapImage; } - public static Task LoadImageAsync(Stream stream) + public static Task LoadImageAsync(Stream stream) { return Task.Run(() => LoadImage(stream)); } - public static BitmapSource LoadImage(byte[] buffer) + public static ImageSource LoadImage(byte[] buffer) { using (var stream = new MemoryStream(buffer)) { @@ -40,24 +41,25 @@ namespace MapControl } } - public static Task LoadImageAsync(byte[] buffer) + public static Task LoadImageAsync(byte[] buffer) { return Task.Run(() => LoadImage(buffer)); } - private static async Task LoadImageAsync(HttpContent content) + private static async Task LoadImageAsync(HttpContent content) { using (var stream = new MemoryStream()) { await content.CopyToAsync(stream); stream.Seek(0, SeekOrigin.Begin); + return await LoadImageAsync(stream); } } - private static BitmapSource LoadLocalImage(Uri uri) + private static ImageSource LoadLocalImage(Uri uri) { - BitmapSource image = null; + ImageSource image = null; var path = uri.IsAbsoluteUri ? uri.LocalPath : uri.OriginalString; if (File.Exists(path)) @@ -71,37 +73,50 @@ namespace MapControl return image; } - private static Task LoadLocalImageAsync(Uri uri) + private static Task LoadLocalImageAsync(Uri uri) { return Task.Run(() => LoadLocalImage(uri)); } - internal static async Task> LoadHttpStreamAsync(Uri uri) + internal class HttpStreamResponse { - Tuple result = null; + public readonly MemoryStream Stream; + public readonly TimeSpan? MaxAge; + + public HttpStreamResponse(MemoryStream stream, TimeSpan? maxAge) + { + Stream = stream; + MaxAge = maxAge; + } + } + + internal static async Task LoadHttpStreamAsync(Uri uri) + { + HttpStreamResponse response = null; try { - using (var response = await HttpClient.GetAsync(uri)) + using (var responseMessage = await HttpClient.GetAsync(uri)) { - if (!response.IsSuccessStatusCode) - { - Debug.WriteLine("ImageLoader: {0}: {1} {2}", uri, (int)response.StatusCode, response.ReasonPhrase); - } - else + if (responseMessage.IsSuccessStatusCode) { MemoryStream stream = null; TimeSpan? maxAge = null; - if (IsTileAvailable(response.Headers)) + if (ImageAvailable(responseMessage.Headers)) { stream = new MemoryStream(); - await response.Content.CopyToAsync(stream); + await responseMessage.Content.CopyToAsync(stream); stream.Seek(0, SeekOrigin.Begin); - maxAge = response.Headers.CacheControl?.MaxAge; + + maxAge = responseMessage.Headers.CacheControl?.MaxAge; } - result = new Tuple(stream, maxAge); + response = new HttpStreamResponse(stream, maxAge); + } + else + { + Debug.WriteLine("ImageLoader: {0}: {1} {2}", uri, (int)responseMessage.StatusCode, responseMessage.ReasonPhrase); } } } @@ -110,10 +125,10 @@ namespace MapControl Debug.WriteLine("ImageLoader: {0}: {1}", uri, ex.Message); } - return result; + return response; } - private static bool IsTileAvailable(HttpResponseHeaders responseHeaders) + private static bool ImageAvailable(HttpResponseHeaders responseHeaders) { IEnumerable tileInfo; return !responseHeaders.TryGetValues("X-VE-Tile-Info", out tileInfo) || !tileInfo.Contains("no-tile"); diff --git a/MapControl/WPF/TileImageLoader.WPF.cs b/MapControl/WPF/TileImageLoader.WPF.cs index 6425715f..da673138 100644 --- a/MapControl/WPF/TileImageLoader.WPF.cs +++ b/MapControl/WPF/TileImageLoader.WPF.cs @@ -34,18 +34,18 @@ namespace MapControl if (cacheBuffer == null || expiration < DateTime.UtcNow) { - var result = await ImageLoader.LoadHttpStreamAsync(uri); + var response = await ImageLoader.LoadHttpStreamAsync(uri).ConfigureAwait(false); - if (result != null) // download succeeded + if (response != null) // download succeeded { cacheBuffer = null; // discard cached image - if (result.Item1 != null) // tile image available + if (response.Stream != null) // tile image available { - using (var stream = result.Item1) + using (var stream = response.Stream) { LoadTileImage(tile, stream); - SetCachedImage(cacheKey, stream, GetExpiration(result.Item2)); + SetCachedImage(cacheKey, stream, GetExpiration(response.MaxAge)); } } } @@ -62,7 +62,7 @@ namespace MapControl private async Task LoadTileImageAsync(Tile tile, TileSource tileSource) { - SetTileImage(tile, await tileSource.LoadImageAsync(tile.XIndex, tile.Y, tile.ZoomLevel)); + SetTileImage(tile, await tileSource.LoadImageAsync(tile.XIndex, tile.Y, tile.ZoomLevel).ConfigureAwait(false)); } private void LoadTileImage(Tile tile, Stream stream) diff --git a/MapImages/Shared/WorldFileImage.cs b/MapImages/Shared/WorldFileImage.cs index 6385b8df..7a63dd1f 100644 --- a/MapImages/Shared/WorldFileImage.cs +++ b/MapImages/Shared/WorldFileImage.cs @@ -80,12 +80,13 @@ namespace MapControl.Images public static async Task ReadWorldFileImage(string imagePath, string worldFilePath, string projFilePath = null) { BitmapSource bitmap; + using (var stream = File.OpenRead(imagePath)) { #if WINDOWS_UWP - bitmap = await ImageLoader.LoadImageAsync(stream.AsRandomAccessStream()); + bitmap = (BitmapSource)await ImageLoader.LoadImageAsync(stream.AsRandomAccessStream()); #else - bitmap = await ImageLoader.LoadImageAsync(stream); + bitmap = (BitmapSource)await ImageLoader.LoadImageAsync(stream); #endif }