From f4593cd325fa2999bdfb5b33795a8e2ed50a6679 Mon Sep 17 00:00:00 2001
From: Lightczx <1686188646@qq.com>
Date: Wed, 15 May 2024 13:39:19 +0800
Subject: [PATCH] refactor cached image
---
.../Snap.Hutao/Control/Image/CachedImage.cs | 10 +-
.../Snap.Hutao/Control/Image/CachedImage.xaml | 1 -
.../Image/Implementation/ImageExBase.cs | 196 ++----------------
.../Snap.Hutao/View/Control/ItemIcon.xaml | 5 +-
.../Snap.Hutao/View/Control/LoadingView.xaml | 1 -
.../Snap.Hutao/View/Page/FeedbackPage.xaml | 1 -
.../Snap.Hutao/View/Page/LaunchGamePage.xaml | 1 -
.../Snap.Hutao/View/Page/WikiAvatarPage.xaml | 2 -
.../Snap.Hutao/View/Page/WikiWeaponPage.xaml | 2 -
9 files changed, 26 insertions(+), 193 deletions(-)
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.cs b/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.cs
index 94792415..29bf5913 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.cs
@@ -23,13 +23,10 @@ internal sealed class CachedImage : Implementation.ImageEx
{
DefaultStyleKey = typeof(CachedImage);
DefaultStyleResourceUri = "ms-appx:///Control/Image/CachedImage.xaml".ToUri();
-
- IsCacheEnabled = true;
- EnableLazyLoading = false;
}
///
- protected override async Task ProvideCachedResourceAsync(Uri imageUri, CancellationToken token)
+ protected override async Task ProvideCachedResourceAsync(Uri imageUri, CancellationToken token)
{
IImageCache imageCache = this.ServiceProvider().GetRequiredService();
@@ -38,10 +35,7 @@ internal sealed class CachedImage : Implementation.ImageEx
HutaoException.ThrowIf(string.IsNullOrEmpty(imageUri.Host), SH.ControlImageCachedImageInvalidResourceUri);
string file = await imageCache.GetFileFromCacheAsync(imageUri).ConfigureAwait(true); // BitmapImage need to be created by main thread.
token.ThrowIfCancellationRequested(); // check token state to determine whether the operation should be canceled.
-
- // https://learn.microsoft.com/en-us/windows/uwp/debug-test-perf/optimize-animations-and-media#optimize-image-resources
- // BitmapImage initialize with a uri will increase image quality and loading speed.
- return new BitmapImage(file.ToUri());
+ return file.ToUri();
}
catch (COMException)
{
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.xaml b/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.xaml
index f74b45dd..372a7f5c 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.xaml
@@ -6,7 +6,6 @@
-
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Image/Implementation/ImageExBase.cs b/src/Snap.Hutao/Snap.Hutao/Control/Image/Implementation/ImageExBase.cs
index ee41e58f..b4cf793b 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/Image/Implementation/ImageExBase.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Image/Implementation/ImageExBase.cs
@@ -21,12 +21,6 @@ namespace Snap.Hutao.Control.Image.Implementation;
[TemplatePart(Name = PartImage, Type = typeof(object))]
[TemplatePart(Name = PartPlaceholderImage, Type = typeof(object))]
[DependencyProperty("Stretch", typeof(Stretch), Stretch.Uniform)]
-[DependencyProperty("DecodePixelHeight", typeof(int), 0)]
-[DependencyProperty("DecodePixelWidth", typeof(int), 0)]
-[DependencyProperty("DecodePixelType", typeof(DecodePixelType), DecodePixelType.Physical)]
-[DependencyProperty("IsCacheEnabled", typeof(bool), false)]
-[DependencyProperty("EnableLazyLoading", typeof(bool), false, nameof(EnableLazyLoadingChanged))]
-[DependencyProperty("LazyLoadingThreshold", typeof(double), default(double), nameof(LazyLoadingThresholdChanged))]
[DependencyProperty("PlaceholderSource", typeof(object), default(object))]
[DependencyProperty("PlaceholderStretch", typeof(Stretch), Stretch.Uniform)]
[DependencyProperty("PlaceholderMargin", typeof(Thickness))]
@@ -42,8 +36,6 @@ internal abstract partial class ImageExBase : Microsoft.UI.Xaml.Controls.Control
protected const string FailedState = "Failed";
private CancellationTokenSource? tokenSource;
- private object? lazyLoadingSource;
- private bool isInViewport;
public bool IsInitialized { get; private set; }
@@ -58,10 +50,10 @@ internal abstract partial class ImageExBase : Microsoft.UI.Xaml.Controls.Control
public abstract CompositionBrush GetAlphaMask();
- protected virtual Task ProvideCachedResourceAsync(Uri imageUri, CancellationToken token)
+ protected virtual Task ProvideCachedResourceAsync(Uri imageUri, CancellationToken token)
{
// By default we just use the built-in UWP image cache provided within the Image control.
- return Task.FromResult(new BitmapImage(imageUri));
+ return Task.FromResult(imageUri);
}
protected virtual void OnImageOpened(object sender, RoutedEventArgs e)
@@ -80,19 +72,10 @@ internal abstract partial class ImageExBase : Microsoft.UI.Xaml.Controls.Control
RemoveImageFailed(OnImageFailed);
Image = GetTemplateChild(PartImage);
- PlaceholderImage = GetTemplateChild(PartPlaceholderImage);
IsInitialized = true;
- if (Source is null || !EnableLazyLoading || isInViewport)
- {
- lazyLoadingSource = null;
- SetSource(Source);
- }
- else
- {
- lazyLoadingSource = Source;
- }
+ SetSource(Source);
AttachImageOpened(OnImageOpened);
AttachImageFailed(OnImageFailed);
@@ -148,33 +131,6 @@ internal abstract partial class ImageExBase : Microsoft.UI.Xaml.Controls.Control
}
}
- private static void EnableLazyLoadingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- if (d is not ImageExBase control)
- {
- return;
- }
-
- bool value = (bool)e.NewValue;
- if (value)
- {
- control.LayoutUpdated += control.OnImageExBaseLayoutUpdated;
- control.InvalidateLazyLoading();
- }
- else
- {
- control.LayoutUpdated -= control.OnImageExBaseLayoutUpdated;
- }
- }
-
- private static void LazyLoadingThresholdChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- if (d is ImageExBase { EnableLazyLoading: true } control)
- {
- control.InvalidateLazyLoading();
- }
- }
-
private static void SourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is not ImageExBase control)
@@ -187,15 +143,7 @@ internal abstract partial class ImageExBase : Microsoft.UI.Xaml.Controls.Control
return;
}
- if (e.NewValue is null || !control.EnableLazyLoading || control.isInViewport)
- {
- control.lazyLoadingSource = null;
- control.SetSource(e.NewValue);
- }
- else
- {
- control.lazyLoadingSource = e.NewValue;
- }
+ control.SetSource(e.NewValue);
}
private static bool IsHttpUri(Uri uri)
@@ -203,11 +151,8 @@ internal abstract partial class ImageExBase : Microsoft.UI.Xaml.Controls.Control
return uri.IsAbsoluteUri && (uri.Scheme == "http" || uri.Scheme == "https");
}
- private void AttachSource(ImageSource? source)
+ private void AttachSource(BitmapImage? source, Uri? uri)
{
- // Setting the source at this point should call ImageExOpened/VisualStateManager.GoToState
- // as we register to both the ImageOpened/ImageFailed events of the underlying control.
- // We only need to call those methods if we fail in other cases before we get here.
if (Image is Microsoft.UI.Xaml.Controls.Image image)
{
image.Source = source;
@@ -221,13 +166,15 @@ internal abstract partial class ImageExBase : Microsoft.UI.Xaml.Controls.Control
{
VisualStateManager.GoToState(this, UnloadedState, true);
}
- else if (source is BitmapSource { PixelHeight: > 0, PixelWidth: > 0 })
+ else
{
+ // https://learn.microsoft.com/en-us/windows/uwp/debug-test-perf/optimize-animations-and-media#optimize-image-resources
+ source.UriSource = uri;
VisualStateManager.GoToState(this, LoadedState, true);
}
}
- private void AttachPlaceholderSource(ImageSource? source)
+ private void AttachPlaceholderSource(BitmapImage? source, Uri? uri)
{
if (PlaceholderImage is Microsoft.UI.Xaml.Controls.Image image)
{
@@ -242,8 +189,10 @@ internal abstract partial class ImageExBase : Microsoft.UI.Xaml.Controls.Control
{
VisualStateManager.GoToState(this, UnloadedState, true);
}
- else if (source is BitmapSource { PixelHeight: > 0, PixelWidth: > 0 })
+ else
{
+ // https://learn.microsoft.com/en-us/windows/uwp/debug-test-perf/optimize-animations-and-media#optimize-image-resources
+ source.UriSource = uri;
VisualStateManager.GoToState(this, LoadedState, true);
}
}
@@ -256,10 +205,9 @@ internal abstract partial class ImageExBase : Microsoft.UI.Xaml.Controls.Control
}
tokenSource?.Cancel();
-
tokenSource = new CancellationTokenSource();
- AttachSource(null);
+ AttachSource(default, default);
if (source is null)
{
@@ -268,13 +216,6 @@ internal abstract partial class ImageExBase : Microsoft.UI.Xaml.Controls.Control
VisualStateManager.GoToState(this, LoadingState, true);
- if (source as ImageSource is { } imageSource)
- {
- AttachSource(imageSource);
-
- return;
- }
-
if (source as Uri is not { } uri)
{
string? url = source as string ?? source.ToString();
@@ -319,20 +260,13 @@ internal abstract partial class ImageExBase : Microsoft.UI.Xaml.Controls.Control
tokenSource?.Cancel();
tokenSource = new();
- AttachPlaceholderSource(null);
+ AttachPlaceholderSource(default, default);
if (source is null)
{
return;
}
- if (source as ImageSource is { } imageSource)
- {
- AttachPlaceholderSource(imageSource);
-
- return;
- }
-
if (source as Uri is not { } uri)
{
string? url = source as string ?? source.ToString();
@@ -354,13 +288,13 @@ internal abstract partial class ImageExBase : Microsoft.UI.Xaml.Controls.Control
return;
}
- ImageSource? img = await ProvideCachedResourceAsync(uri, tokenSource.Token).ConfigureAwait(true);
+ Uri? actualUri = await ProvideCachedResourceAsync(uri, tokenSource.Token).ConfigureAwait(true);
ArgumentNullException.ThrowIfNull(tokenSource);
if (!tokenSource.IsCancellationRequested)
{
// Only attach our image if we still have a valid request.
- AttachPlaceholderSource(img);
+ AttachPlaceholderSource(new BitmapImage(), actualUri);
}
}
catch (OperationCanceledException)
@@ -379,99 +313,13 @@ internal abstract partial class ImageExBase : Microsoft.UI.Xaml.Controls.Control
return;
}
- if (IsCacheEnabled)
+ Uri? actualUri = await ProvideCachedResourceAsync(imageUri, token).ConfigureAwait(true);
+
+ ArgumentNullException.ThrowIfNull(tokenSource);
+ if (!tokenSource.IsCancellationRequested)
{
- ImageSource? img = await ProvideCachedResourceAsync(imageUri, token).ConfigureAwait(true);
-
- ArgumentNullException.ThrowIfNull(tokenSource);
- if (!tokenSource.IsCancellationRequested)
- {
- // Only attach our image if we still have a valid request.
- AttachSource(img);
- }
- }
- else if (string.Equals(imageUri.Scheme, "data", StringComparison.OrdinalIgnoreCase))
- {
- string source = imageUri.OriginalString;
- const string base64Head = "base64,";
- int index = source.IndexOf(base64Head, StringComparison.Ordinal);
- if (index >= 0)
- {
- byte[] bytes = Convert.FromBase64String(source[(index + base64Head.Length)..]);
- BitmapImage bitmap = new();
- await bitmap.SetSourceAsync(new MemoryStream(bytes).AsRandomAccessStream());
-
- ArgumentNullException.ThrowIfNull(tokenSource);
- if (!tokenSource.IsCancellationRequested)
- {
- AttachSource(bitmap);
- }
- }
- }
- else
- {
- AttachSource(new BitmapImage(imageUri)
- {
- CreateOptions = BitmapCreateOptions.IgnoreImageCache,
- });
- }
- }
-
- private void OnImageExBaseLayoutUpdated(object? sender, object e)
- {
- InvalidateLazyLoading();
- }
-
- private void InvalidateLazyLoading()
- {
- if (!IsLoaded)
- {
- isInViewport = false;
- return;
- }
-
- // Find the first ascendant ScrollViewer, if not found, use the root element.
- FrameworkElement? hostElement = default;
- IEnumerable ascendants = this.FindAscendants().OfType();
- foreach (FrameworkElement ascendant in ascendants)
- {
- hostElement = ascendant;
- if (hostElement is Microsoft.UI.Xaml.Controls.ScrollViewer)
- {
- break;
- }
- }
-
- if (hostElement is null)
- {
- isInViewport = false;
- return;
- }
-
- Rect controlRect = TransformToVisual(hostElement).TransformBounds(StructMarshal.Rect(ActualSize));
- double lazyLoadingThreshold = LazyLoadingThreshold;
-
- // Left/Top 1 Threshold, Right/Bottom 2 Threshold
- Rect hostRect = new(
- 0 - lazyLoadingThreshold,
- 0 - lazyLoadingThreshold,
- hostElement.ActualWidth + (2 * lazyLoadingThreshold),
- hostElement.ActualHeight + (2 * lazyLoadingThreshold));
-
- if (controlRect.IntersectsWith(hostRect))
- {
- isInViewport = true;
-
- if (lazyLoadingSource is not null)
- {
- object source = lazyLoadingSource;
- lazyLoadingSource = null;
- SetSource(source);
- }
- }
- else
- {
- isInViewport = false;
+ // Only attach our image if we still have a valid request.
+ AttachSource(new BitmapImage(), actualUri);
}
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Control/ItemIcon.xaml b/src/Snap.Hutao/Snap.Hutao/View/Control/ItemIcon.xaml
index 6a28e0de..1f71de94 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Control/ItemIcon.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/View/Control/ItemIcon.xaml
@@ -15,15 +15,14 @@
-
-
+
+
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Control/LoadingView.xaml b/src/Snap.Hutao/Snap.Hutao/View/Control/LoadingView.xaml
index a82a1f0d..a9934b3f 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Control/LoadingView.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/View/Control/LoadingView.xaml
@@ -17,7 +17,6 @@