diff --git a/MapControl/Avalonia/ImageLoader.Avalonia.cs b/MapControl/Avalonia/ImageLoader.Avalonia.cs index b6ca7b0c..27e71872 100644 --- a/MapControl/Avalonia/ImageLoader.Avalonia.cs +++ b/MapControl/Avalonia/ImageLoader.Avalonia.cs @@ -4,7 +4,6 @@ using Avalonia.Media.Imaging; using Avalonia.Platform; using System; using System.IO; -using System.Threading; using System.Threading.Tasks; namespace MapControl @@ -39,18 +38,17 @@ namespace MapControl } } - internal static async Task LoadMergedImageAsync(Uri uri1, Uri uri2, IProgress progress, CancellationToken cancellationToken) + internal static async Task LoadMergedImageAsync(Uri uri1, Uri uri2, IProgress progress) { WriteableBitmap mergedBitmap = null; var p1 = 0d; var p2 = 0d; var images = await Task.WhenAll( - LoadImageAsync(uri1, new Progress(p => { p1 = p; progress.Report((p1 + p2) / 2d); }), cancellationToken), - LoadImageAsync(uri2, new Progress(p => { p2 = p; progress.Report((p1 + p2) / 2d); }), cancellationToken)); + LoadImageAsync(uri1, new Progress(p => { p1 = p; progress.Report((p1 + p2) / 2d); })), + LoadImageAsync(uri2, new Progress(p => { p2 = p; progress.Report((p1 + p2) / 2d); }))); - if (!cancellationToken.IsCancellationRequested && - images.Length == 2 && + if (images.Length == 2 && images[0] is Bitmap bitmap1 && images[1] is Bitmap bitmap2 && bitmap1.PixelSize.Height == bitmap2.PixelSize.Height && diff --git a/MapControl/Shared/ImageLoader.cs b/MapControl/Shared/ImageLoader.cs index c651a9a7..fa05efdb 100644 --- a/MapControl/Shared/ImageLoader.cs +++ b/MapControl/Shared/ImageLoader.cs @@ -2,7 +2,6 @@ using System; using System.IO; using System.Net.Http; -using System.Threading; using System.Threading.Tasks; #if WPF using System.Windows.Media; @@ -28,16 +27,11 @@ namespace MapControl static ImageLoader() { - HttpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(10) }; + HttpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(30) }; HttpClient.DefaultRequestHeaders.Add("User-Agent", $"XAML-Map-Control/{typeof(ImageLoader).Assembly.GetName().Version}"); } - public static Task LoadImageAsync(Uri uri, IProgress progress = null) - { - return LoadImageAsync(uri, progress, CancellationToken.None); - } - - public static async Task LoadImageAsync(Uri uri, IProgress progress, CancellationToken cancellationToken) + public static async Task LoadImageAsync(Uri uri, IProgress progress = null) { ImageSource image = null; @@ -47,7 +41,7 @@ namespace MapControl { if (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps) { - var response = await GetHttpResponseAsync(uri, progress, cancellationToken); + var response = await GetHttpResponseAsync(uri, progress); if (response?.Buffer != null) { @@ -93,19 +87,26 @@ namespace MapControl } } - internal static async Task GetHttpResponseAsync(Uri uri, IProgress progress, CancellationToken cancellationToken) + internal static async Task GetHttpResponseAsync(Uri uri, IProgress progress = null) { HttpResponse response = null; try { - var completionOptions = progress != null ? HttpCompletionOption.ResponseHeadersRead : HttpCompletionOption.ResponseContentRead; - - using (var responseMessage = await HttpClient.GetAsync(uri, completionOptions, cancellationToken).ConfigureAwait(false)) + using (var responseMessage = await HttpClient.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false)) { if (responseMessage.IsSuccessStatusCode) { - byte[] buffer = await responseMessage.Content.ReadAsByteArrayAsync(progress, cancellationToken).ConfigureAwait(false); + byte[] buffer; + + if (progress != null && responseMessage.Content.Headers.ContentLength.HasValue) + { + buffer = await ReadAsByteArray(responseMessage.Content, progress).ConfigureAwait(false); + } + else + { + buffer = await responseMessage.Content.ReadAsByteArrayAsync().ConfigureAwait(false); + } response = new HttpResponse(buffer, responseMessage.Headers.CacheControl?.MaxAge); } @@ -115,17 +116,6 @@ namespace MapControl } } } - catch (OperationCanceledException ex) - { - if (ex.CancellationToken.IsCancellationRequested) - { - Logger?.LogTrace("Cancelled loading image from {uri}", uri); - } - else - { - Logger?.LogError(ex, "Failed loading image from {uri}", uri); - } - } catch (Exception ex) { Logger?.LogError(ex, "Failed loading image from {uri}", uri); @@ -133,60 +123,30 @@ namespace MapControl return response; } - } - internal static class HttpContentExtensions - { - public static async Task ReadAsByteArrayAsync(this HttpContent content, IProgress progress, CancellationToken cancellationToken) + private static async Task ReadAsByteArray(HttpContent content, IProgress progress) { - byte[] buffer; + var length = (int)content.Headers.ContentLength.Value; + var buffer = new byte[length]; - if (progress != null && content.Headers.ContentLength.HasValue) + using (var stream = await content.ReadAsStreamAsync().ConfigureAwait(false)) { - var length = (int)content.Headers.ContentLength.Value; - buffer = new byte[length]; + int offset = 0; + int read; - using (var stream = await content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false)) + while (offset < length && + (read = await stream.ReadAsync(buffer, offset, length - offset).ConfigureAwait(false)) > 0) { - int offset = 0; - int read; + offset += read; - while (offset < length && - (read = await stream.ReadAsync(buffer, offset, length - offset, cancellationToken).ConfigureAwait(false)) > 0) + if (offset < length) // 1.0 reported by caller { - cancellationToken.ThrowIfCancellationRequested(); - - offset += read; - - if (offset < length) // 1.0 reported by caller - { - progress.Report((double)offset / length); - } + progress.Report((double)offset / length); } } } - else - { - buffer = await content.ReadAsByteArrayAsync(cancellationToken).ConfigureAwait(false); - } return buffer; } - -#if !NET - public static Task ReadAsByteArrayAsync(this HttpContent content, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - return content.ReadAsByteArrayAsync(); - } - - public static Task ReadAsStreamAsync(this HttpContent content, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - return content.ReadAsStreamAsync(); - } -#endif } } \ No newline at end of file diff --git a/MapControl/Shared/MapImageLayer.cs b/MapControl/Shared/MapImageLayer.cs index 518a0a60..5b7454a9 100644 --- a/MapControl/Shared/MapImageLayer.cs +++ b/MapControl/Shared/MapImageLayer.cs @@ -1,6 +1,5 @@ using System; using System.Linq; -using System.Threading; using System.Threading.Tasks; #if WPF using System.Windows; @@ -54,7 +53,7 @@ namespace MapControl private readonly Progress loadingProgress; private readonly DispatcherTimer updateTimer; - private CancellationTokenSource cancellationTokenSource; + private bool updateInProgress; public MapImageLayer() { @@ -166,38 +165,39 @@ namespace MapControl } } - protected abstract Task GetImageAsync(BoundingBox boundingBox, IProgress progress, CancellationToken cancellationToken); + protected abstract Task GetImageAsync(BoundingBox boundingBox, IProgress progress); protected async Task UpdateImageAsync() { - updateTimer.Stop(); - - cancellationTokenSource?.Cancel(); - - ClearValue(LoadingProgressProperty); - - if (ParentMap != null && ParentMap.ActualWidth > 0d && ParentMap.ActualHeight > 0d) + if (updateInProgress) { - var width = ParentMap.ActualWidth * RelativeImageSize; - var height = ParentMap.ActualHeight * RelativeImageSize; - var x = (ParentMap.ActualWidth - width) / 2d; - var y = (ParentMap.ActualHeight - height) / 2d; + // Update image on next tick, start timer if not running. + // + updateTimer.Run(); + } + else + { + updateInProgress = true; + updateTimer.Stop(); - var boundingBox = ParentMap.ViewRectToBoundingBox(new Rect(x, y, width, height)); + ImageSource image = null; + BoundingBox boundingBox = null; - ImageSource image; - - using (cancellationTokenSource = new CancellationTokenSource()) + if (ParentMap != null && ParentMap.ActualWidth > 0d && ParentMap.ActualHeight > 0d) { - image = await GetImageAsync(boundingBox, loadingProgress, cancellationTokenSource.Token); + var width = ParentMap.ActualWidth * RelativeImageSize; + var height = ParentMap.ActualHeight * RelativeImageSize; + var x = (ParentMap.ActualWidth - width) / 2d; + var y = (ParentMap.ActualHeight - height) / 2d; + + boundingBox = ParentMap.ViewRectToBoundingBox(new Rect(x, y, width, height)); + + image = await GetImageAsync(boundingBox, loadingProgress); } - cancellationTokenSource = null; + SwapImages(image, boundingBox); - if (image != null && Children.Count >= 2) - { - SwapImages(image, boundingBox); - } + updateInProgress = false; } } @@ -212,22 +212,25 @@ namespace MapControl private void SwapImages(ImageSource image, BoundingBox boundingBox) { - var topImage = (Image)Children[0]; - - Children.RemoveAt(0); - Children.Insert(1, topImage); - - topImage.Source = image; - SetBoundingBox(topImage, boundingBox); - - if (MapBase.ImageFadeDuration > TimeSpan.Zero) + if (Children.Count >= 2) { - FadeOver(); - } - else - { - topImage.Opacity = 1d; - Children[0].Opacity = 0d; + var topImage = (Image)Children[0]; + + Children.RemoveAt(0); + Children.Insert(1, topImage); + + topImage.Source = image; + SetBoundingBox(topImage, boundingBox); + + if (MapBase.ImageFadeDuration > TimeSpan.Zero) + { + FadeOver(); + } + else + { + topImage.Opacity = 1d; + Children[0].Opacity = 0d; + } } } } diff --git a/MapControl/Shared/TileImageLoader.cs b/MapControl/Shared/TileImageLoader.cs index b4270655..ab55c99e 100644 --- a/MapControl/Shared/TileImageLoader.cs +++ b/MapControl/Shared/TileImageLoader.cs @@ -192,7 +192,7 @@ namespace MapControl if (buffer == null) { - var response = await ImageLoader.GetHttpResponseAsync(uri, null, CancellationToken.None).ConfigureAwait(false); + var response = await ImageLoader.GetHttpResponseAsync(uri).ConfigureAwait(false); if (response != null) { diff --git a/MapControl/Shared/WmsImageLayer.cs b/MapControl/Shared/WmsImageLayer.cs index 092c289e..fe58de6c 100644 --- a/MapControl/Shared/WmsImageLayer.cs +++ b/MapControl/Shared/WmsImageLayer.cs @@ -5,8 +5,6 @@ using System.Globalization; using System.Linq; using System.Threading.Tasks; using System.Xml.Linq; -using System.Threading; - #if WPF using System.Windows; using System.Windows.Media; @@ -164,7 +162,7 @@ namespace MapControl /// /// Loads an ImageSource from the URL returned by GetMapRequestUri(). /// - protected override async Task GetImageAsync(BoundingBox boundingBox, IProgress progress, CancellationToken cancellationToken) + protected override async Task GetImageAsync(BoundingBox boundingBox, IProgress progress) { ImageSource image = null; @@ -187,7 +185,7 @@ namespace MapControl if (uri != null) { - image = await ImageLoader.LoadImageAsync(new Uri(uri), progress, cancellationToken); + image = await ImageLoader.LoadImageAsync(new Uri(uri), progress); } } else @@ -210,7 +208,7 @@ namespace MapControl if (uri1 != null && uri2 != null) { - image = await ImageLoader.LoadMergedImageAsync(new Uri(uri1), new Uri(uri2), progress, cancellationToken); + image = await ImageLoader.LoadMergedImageAsync(new Uri(uri1), new Uri(uri2), progress); } } } diff --git a/MapControl/WPF/ImageLoader.WPF.cs b/MapControl/WPF/ImageLoader.WPF.cs index 9371d50a..3b56b766 100644 --- a/MapControl/WPF/ImageLoader.WPF.cs +++ b/MapControl/WPF/ImageLoader.WPF.cs @@ -1,6 +1,5 @@ using System; using System.IO; -using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Media; @@ -46,18 +45,17 @@ namespace MapControl } } - internal static async Task LoadMergedImageAsync(Uri uri1, Uri uri2, IProgress progress, CancellationToken cancellationToken) + internal static async Task LoadMergedImageAsync(Uri uri1, Uri uri2, IProgress progress) { WriteableBitmap mergedBitmap = null; var p1 = 0d; var p2 = 0d; var images = await Task.WhenAll( - LoadImageAsync(uri1, new Progress(p => { p1 = p; progress.Report((p1 + p2) / 2d); }), cancellationToken), - LoadImageAsync(uri2, new Progress(p => { p2 = p; progress.Report((p1 + p2) / 2d); }), cancellationToken)); + LoadImageAsync(uri1, new Progress(p => { p1 = p; progress.Report((p1 + p2) / 2d); })), + LoadImageAsync(uri2, new Progress(p => { p2 = p; progress.Report((p1 + p2) / 2d); }))); - if (!cancellationToken.IsCancellationRequested && - images.Length == 2 && + if (images.Length == 2 && images[0] is BitmapSource bitmap1 && images[1] is BitmapSource bitmap2 && bitmap1.PixelHeight == bitmap2.PixelHeight && diff --git a/MapControl/WinUI/ImageLoader.WinUI.cs b/MapControl/WinUI/ImageLoader.WinUI.cs index f4976b32..848e3f0c 100644 --- a/MapControl/WinUI/ImageLoader.WinUI.cs +++ b/MapControl/WinUI/ImageLoader.WinUI.cs @@ -2,7 +2,6 @@ using System; using System.IO; using System.Runtime.InteropServices.WindowsRuntime; -using System.Threading; using System.Threading.Tasks; using Windows.Graphics.Imaging; using Windows.Storage; @@ -67,7 +66,7 @@ namespace MapControl return image; } - internal static async Task LoadWriteableBitmapAsync(Uri uri, IProgress progress, CancellationToken cancellationToken) + internal static async Task LoadWriteableBitmapAsync(Uri uri, IProgress progress) { WriteableBitmap bitmap = null; @@ -75,7 +74,7 @@ namespace MapControl try { - var response = await GetHttpResponseAsync(uri, progress, cancellationToken); + var response = await GetHttpResponseAsync(uri, progress); if (response?.Buffer != null) { @@ -98,18 +97,17 @@ namespace MapControl return bitmap; } - internal static async Task LoadMergedImageAsync(Uri uri1, Uri uri2, IProgress progress, CancellationToken cancellationToken) + internal static async Task LoadMergedImageAsync(Uri uri1, Uri uri2, IProgress progress) { WriteableBitmap mergedBitmap = null; var p1 = 0d; var p2 = 0d; var bitmaps = await Task.WhenAll( - LoadWriteableBitmapAsync(uri1, new Progress(p => { p1 = p; progress.Report((p1 + p2) / 2d); }), cancellationToken), - LoadWriteableBitmapAsync(uri2, new Progress(p => { p2 = p; progress.Report((p1 + p2) / 2d); }), cancellationToken)); + LoadWriteableBitmapAsync(uri1, new Progress(p => { p1 = p; progress.Report((p1 + p2) / 2d); })), + LoadWriteableBitmapAsync(uri2, new Progress(p => { p2 = p; progress.Report((p1 + p2) / 2d); }))); - if (!cancellationToken.IsCancellationRequested && - bitmaps.Length == 2 && + if (bitmaps.Length == 2 && bitmaps[0] != null && bitmaps[1] != null && bitmaps[0].PixelHeight == bitmaps[1].PixelHeight)