Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ obj/
/packages/
riderModule.iml
/_ReSharper.Caches/
.idea/
.idea/
*.DotSettings*
6 changes: 6 additions & 0 deletions AsyncImageLoader.Avalonia/AdvancedImage.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,12 @@ private async void UpdateImage(string? source, IAsyncImageLoader? loader)
}

loader ??= ImageLoader.AsyncImageLoader;

if (loader is IAdvancedAsyncImageLoader advancedLoader)
{
return await advancedLoader.ProvideImageAsync(source, TopLevel.GetTopLevel(this)?.StorageProvider);
}

return await loader.ProvideImageAsync(source);
}
catch (TaskCanceledException)
Expand Down
17 changes: 17 additions & 0 deletions AsyncImageLoader.Avalonia/IAdvancedAsyncImageLoader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System;
using System.Threading.Tasks;
using Avalonia.Media.Imaging;
using Avalonia.Platform.Storage;

namespace AsyncImageLoader;

public interface IAdvancedAsyncImageLoader : IDisposable
{
/// <summary>
/// Loads image
/// </summary>
/// <param name="url">Target url</param>
/// <param name="storageProvider">Avalonia's storage provider</param>
/// <returns>Bitmap</returns>
public Task<Bitmap?> ProvideImageAsync(string url, IStorageProvider? storageProvider = null);
}
5 changes: 5 additions & 0 deletions AsyncImageLoader.Avalonia/ImageLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ private static async void OnSourceChanged(Image sender, AvaloniaPropertyChangedE
// A small delay allows to cancel early if the image goes out of screen too fast (eg. scrolling)
// The Bitmap constructor is expensive and cannot be cancelled
await Task.Delay(10, cts.Token);

if (AsyncImageLoader is IAdvancedAsyncImageLoader advancedLoader)
{
return await advancedLoader.ProvideImageAsync(url, TopLevel.GetTopLevel(sender)?.StorageProvider);
}

return await AsyncImageLoader.ProvideImageAsync(url);
}
Expand Down
48 changes: 38 additions & 10 deletions AsyncImageLoader.Avalonia/Loaders/BaseWebImageLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Logging;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
using IStorageProvider = Avalonia.Platform.Storage.IStorageProvider;

namespace AsyncImageLoader.Loaders;

/// <summary>
/// Provides non cached way to asynchronously load images for <see cref="ImageLoader" />
/// Can be used as base class if you want to create custom caching mechanism
/// </summary>
public class BaseWebImageLoader : IAsyncImageLoader {
public class BaseWebImageLoader : IAsyncImageLoader, IAdvancedAsyncImageLoader {
private readonly ParametrizedLogger? _logger;
private readonly bool _shouldDisposeHttpClient;

Expand Down Expand Up @@ -48,16 +51,23 @@ public void Dispose() {
GC.SuppressFinalize(this);
}

/// <inheritdoc />
public async Task<Bitmap?> ProvideImageAsync(string url, IStorageProvider? storageProvider = null)
{
return await LoadAsync(url, storageProvider).ConfigureAwait(false);
}

/// <summary>
/// Attempts to load bitmap
/// </summary>
/// <param name="url">Target url</param>
/// <param name="storageProvider">Avalonia's storage provider</param>
/// <returns>Bitmap</returns>
protected virtual async Task<Bitmap?> LoadAsync(string url) {
protected virtual async Task<Bitmap?> LoadAsync(string url, IStorageProvider? storageProvider) {
var internalOrCachedBitmap =
await LoadFromLocalAsync(url).ConfigureAwait(false)
?? await LoadFromInternalAsync(url).ConfigureAwait(false)
?? await LoadFromGlobalCache(url).ConfigureAwait(false);
await LoadFromLocalAsync(url, storageProvider).ConfigureAwait(false)
?? await LoadFromInternalAsync(url).ConfigureAwait(false)
?? await LoadFromGlobalCache(url).ConfigureAwait(false);
if (internalOrCachedBitmap != null) return internalOrCachedBitmap;

try {
Expand All @@ -78,12 +88,30 @@ await LoadFromLocalAsync(url).ConfigureAwait(false)
}

/// <summary>
/// the url maybe is local file url,so if file exists ,we got a Bitmap
/// Attempts to load bitmap
/// </summary>
/// <param name="url">Target url</param>
/// <returns>Bitmap</returns>
protected virtual Task<Bitmap?> LoadAsync(string url)
{
return LoadAsync(url, null);
}

/// <summary>
/// The url maybe is local file url, so if file exists, we got a Bitmap
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
private Task<Bitmap?> LoadFromLocalAsync(string url) {
return Task.FromResult(File.Exists(url) ? new Bitmap(url) : null);
/// <param name="url">Url to load</param>
/// <param name="storageProvider">Avalonia's storage provider</param>
private static async Task<Bitmap?> LoadFromLocalAsync(string url, IStorageProvider? storageProvider)
{
if (File.Exists(url))
return new Bitmap(url);

if (storageProvider is null) return null;
var fileInfo = await storageProvider.TryGetFileFromPathAsync(new Uri(url));
if (fileInfo is null) return null;
using var fileStream = await fileInfo.OpenReadAsync();
return new Bitmap(fileStream);
}

/// <summary>
Expand Down