mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
custom background image
This commit is contained in:
@@ -33,6 +33,16 @@ internal struct Bgra32
|
||||
/// </summary>
|
||||
public byte A;
|
||||
|
||||
public Bgra32(byte b, byte g, byte r, byte a)
|
||||
{
|
||||
B = b;
|
||||
G = g;
|
||||
R = r;
|
||||
A = a;
|
||||
}
|
||||
|
||||
public readonly double Luminance { get => ((0.299 * R) + (0.587 * G) + (0.114 * B)) / 255; }
|
||||
|
||||
/// <summary>
|
||||
/// 从 Color 转换
|
||||
/// </summary>
|
||||
@@ -44,4 +54,11 @@ internal struct Bgra32
|
||||
*(uint*)&bgra8 = BinaryPrimitives.ReverseEndianness(*(uint*)&color);
|
||||
return bgra8;
|
||||
}
|
||||
|
||||
public static unsafe implicit operator Color(Bgra32 bgra8)
|
||||
{
|
||||
Unsafe.SkipInit(out Color color);
|
||||
*(uint*)&color = BinaryPrimitives.ReverseEndianness(*(uint*)&bgra8);
|
||||
return color;
|
||||
}
|
||||
}
|
||||
@@ -38,4 +38,43 @@ internal static class SoftwareBitmapExtension
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static unsafe double Luminance(this SoftwareBitmap softwareBitmap)
|
||||
{
|
||||
using (BitmapBuffer buffer = softwareBitmap.LockBuffer(BitmapBufferAccessMode.Read))
|
||||
{
|
||||
using (IMemoryBufferReference reference = buffer.CreateReference())
|
||||
{
|
||||
reference.As<IMemoryBufferByteAccess>().GetBuffer(out Span<Bgra32> bytes);
|
||||
double sum = 0;
|
||||
foreach (ref readonly Bgra32 pixel in bytes)
|
||||
{
|
||||
sum += pixel.Luminance;
|
||||
}
|
||||
|
||||
return sum / bytes.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static unsafe Bgra32 GetAccentColor(this SoftwareBitmap softwareBitmap)
|
||||
{
|
||||
using (BitmapBuffer buffer = softwareBitmap.LockBuffer(BitmapBufferAccessMode.Read))
|
||||
{
|
||||
using (IMemoryBufferReference reference = buffer.CreateReference())
|
||||
{
|
||||
reference.As<IMemoryBufferByteAccess>().GetBuffer(out Span<Bgra32> bytes);
|
||||
double b = 0, g = 0, r = 0, a = 0;
|
||||
foreach (ref readonly Bgra32 pixel in bytes)
|
||||
{
|
||||
b += pixel.B;
|
||||
g += pixel.G;
|
||||
r += pixel.R;
|
||||
a += pixel.A;
|
||||
}
|
||||
|
||||
return new((byte)(b / bytes.Length), (byte)(g / bytes.Length), (byte)(r / bytes.Length), (byte)(a / bytes.Length));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,4 +18,9 @@ internal static class RuntimeOptionsExtension
|
||||
{
|
||||
return Path.Combine(options.DataFolder, "ServerCache");
|
||||
}
|
||||
|
||||
public static string GetDataFolderBackgroundFolder(this RuntimeOptions options)
|
||||
{
|
||||
return Path.Combine(options.DataFolder, "Background");
|
||||
}
|
||||
}
|
||||
@@ -2528,6 +2528,12 @@
|
||||
<data name="ViewPageSettingOfficialSiteNavigate" xml:space="preserve">
|
||||
<value>前往官网</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingOpenBackgroundImageFolderDescription" xml:space="preserve">
|
||||
<value>自定义背景图片,支持 bmp / gif / ico / jpg / jpeg / png / tiff / webp 格式</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingOpenBackgroundImageFolderHeader" xml:space="preserve">
|
||||
<value>打开背景图片文件夹</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingResetAction" xml:space="preserve">
|
||||
<value>重置</value>
|
||||
</data>
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Xaml.Media.Imaging;
|
||||
using Snap.Hutao.Control.Media;
|
||||
using Snap.Hutao.Core;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using Windows.Graphics.Imaging;
|
||||
using Windows.UI;
|
||||
|
||||
namespace Snap.Hutao.Service.BackgroundImage;
|
||||
|
||||
internal sealed class BackgroundImage
|
||||
{
|
||||
public BitmapImage ImageSource { get; set; } = default!;
|
||||
|
||||
public Color AccentColor { get; set; }
|
||||
|
||||
public double Luminance { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Control.Media;
|
||||
using Snap.Hutao.Core;
|
||||
using System.IO;
|
||||
using Windows.Graphics.Imaging;
|
||||
|
||||
namespace Snap.Hutao.Service.BackgroundImage;
|
||||
|
||||
[ConstructorGenerated]
|
||||
[Injection(InjectAs.Singleton, typeof(IBackgroundImageService))]
|
||||
internal sealed partial class BackgroundImageService : IBackgroundImageService
|
||||
{
|
||||
private static readonly HashSet<string> AllowedFormats = [".bmp", ".gif", ".ico", ".jpg", ".jpeg", ".png", ".tiff", ".webp"];
|
||||
|
||||
private readonly ITaskContext taskContext;
|
||||
private readonly RuntimeOptions runtimeOptions;
|
||||
|
||||
private HashSet<string> backgroundPathMap;
|
||||
|
||||
public async ValueTask<ValueResult<bool, BackgroundImage>> GetNextBackgroundImageAsync()
|
||||
{
|
||||
HashSet<string> backgroundSet = SkipOrInitBackground();
|
||||
|
||||
if (backgroundSet.Count <= 0)
|
||||
{
|
||||
return new(false, default!);
|
||||
}
|
||||
|
||||
string path = System.Random.Shared.GetItems(backgroundSet.ToArray(), 1)[0];
|
||||
backgroundSet.Remove(path);
|
||||
using (FileStream fileStream = File.OpenRead(path))
|
||||
{
|
||||
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(fileStream.AsRandomAccessStream());
|
||||
SoftwareBitmap softwareBitmap = await decoder.GetSoftwareBitmapAsync(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Straight);
|
||||
Bgra32 accentColor = softwareBitmap.GetAccentColor();
|
||||
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
|
||||
BackgroundImage background = new()
|
||||
{
|
||||
ImageSource = new(path.ToUri()),
|
||||
AccentColor = accentColor,
|
||||
Luminance = accentColor.Luminance,
|
||||
};
|
||||
|
||||
return new(true, background);
|
||||
}
|
||||
}
|
||||
|
||||
private HashSet<string> SkipOrInitBackground()
|
||||
{
|
||||
if (backgroundPathMap is null || backgroundPathMap.Count <= 0)
|
||||
{
|
||||
string backgroundFolder = runtimeOptions.GetDataFolderBackgroundFolder();
|
||||
Directory.CreateDirectory(backgroundFolder);
|
||||
backgroundPathMap = Directory
|
||||
.GetFiles(backgroundFolder, "*.*", SearchOption.AllDirectories)
|
||||
.Where(path => AllowedFormats.Contains(Path.GetExtension(path)))
|
||||
.ToHashSet();
|
||||
}
|
||||
|
||||
return backgroundPathMap;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
|
||||
namespace Snap.Hutao.Service.BackgroundImage;
|
||||
|
||||
internal interface IBackgroundImageService
|
||||
{
|
||||
ValueTask<ValueResult<bool, BackgroundImage>> GetNextBackgroundImageAsync();
|
||||
}
|
||||
@@ -321,7 +321,7 @@
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.4.231219000" />
|
||||
<PackageReference Include="QRCoder" Version="1.4.3" />
|
||||
<PackageReference Include="Snap.Discord.GameSDK" Version="1.6.0" />
|
||||
<PackageReference Include="Snap.Hutao.Deployment.Runtime" Version="1.14.0">
|
||||
<PackageReference Include="Snap.Hutao.Deployment.Runtime" Version="1.15.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -12,14 +12,15 @@
|
||||
mc:Ignorable="d">
|
||||
<UserControl.Resources>
|
||||
<Thickness x:Key="NavigationViewContentMargin">0,44,0,0</Thickness>
|
||||
<Thickness x:Key="NavigationViewContentGridBorderThickness">0,1,0,0</Thickness>
|
||||
<x:Double x:Key="NavigationViewItemOnLeftIconBoxHeight">24</x:Double>
|
||||
<SolidColorBrush x:Key="NavigationViewExpandedPaneBackground" Color="Transparent"/>
|
||||
<Thickness x:Key="NavigationViewContentGridBorderThickness">0,1,0,0</Thickness>
|
||||
</UserControl.Resources>
|
||||
<Grid Transitions="{ThemeResource EntranceThemeTransitions}">
|
||||
<Grid Background="{ThemeResource SolidBackgroundFillColorBaseBrush}" Transitions="{ThemeResource EntranceThemeTransitions}">
|
||||
<Image
|
||||
Opacity="0.65"
|
||||
Source="ms-appx:///Resource/TestBackground.jpg"
|
||||
x:Name="BackdroundImagePresenter"
|
||||
VerticalAlignment="Center"
|
||||
Opacity="0"
|
||||
Stretch="UniformToFill"/>
|
||||
|
||||
<NavigationView
|
||||
@@ -109,4 +110,4 @@
|
||||
|
||||
<shv:InfoBarView/>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
</UserControl>
|
||||
@@ -1,7 +1,9 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.WinUI.Animations;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Snap.Hutao.Service.BackgroundImage;
|
||||
using Snap.Hutao.Service.Navigation;
|
||||
using Snap.Hutao.View.Page;
|
||||
|
||||
@@ -14,6 +16,7 @@ namespace Snap.Hutao.View;
|
||||
internal sealed partial class MainView : UserControl
|
||||
{
|
||||
private readonly INavigationService navigationService;
|
||||
private readonly IBackgroundImageService backgroundImageService;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的主视图
|
||||
@@ -24,6 +27,9 @@ internal sealed partial class MainView : UserControl
|
||||
|
||||
IServiceProvider serviceProvider = Ioc.Default;
|
||||
|
||||
backgroundImageService = serviceProvider.GetRequiredService<IBackgroundImageService>();
|
||||
RunBackgroundImageLoopAsync(serviceProvider.GetRequiredService<ITaskContext>()).SafeForget();
|
||||
|
||||
navigationService = serviceProvider.GetRequiredService<INavigationService>();
|
||||
navigationService
|
||||
.As<INavigationInitialization>()?
|
||||
@@ -31,4 +37,35 @@ internal sealed partial class MainView : UserControl
|
||||
|
||||
navigationService.Navigate<AnnouncementPage>(INavigationAwaiter.Default, true);
|
||||
}
|
||||
|
||||
private async ValueTask RunBackgroundImageLoopAsync(ITaskContext taskContext)
|
||||
{
|
||||
using (PeriodicTimer timer = new(TimeSpan.FromMinutes(5)))
|
||||
{
|
||||
do
|
||||
{
|
||||
(bool isOk, BackgroundImage backgroundImage) = await backgroundImageService.GetNextBackgroundImageAsync().ConfigureAwait(false);
|
||||
|
||||
if (isOk)
|
||||
{
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
|
||||
await AnimationBuilder
|
||||
.Create()
|
||||
.Opacity(to: 0D, duration: TimeSpan.FromMilliseconds(300), easingType: EasingType.Sine)
|
||||
.StartAsync(BackdroundImagePresenter)
|
||||
.ConfigureAwait(true);
|
||||
|
||||
BackdroundImagePresenter.Source = backgroundImage.ImageSource;
|
||||
double targetOpacity = (1 - backgroundImage.Luminance) * 0.8;
|
||||
|
||||
await AnimationBuilder
|
||||
.Create()
|
||||
.Opacity(to: targetOpacity, duration: TimeSpan.FromMilliseconds(300), easingType: EasingType.Sine)
|
||||
.StartAsync(BackdroundImagePresenter)
|
||||
.ConfigureAwait(true);
|
||||
}
|
||||
} while (await timer.WaitForNextTickAsync().ConfigureAwait(false));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -455,7 +455,7 @@
|
||||
<Border cw:Effects.Shadow="{ThemeResource CompatCardShadow}">
|
||||
<Border Padding="16" Style="{ThemeResource AcrylicBorderCardStyle}">
|
||||
<StackPanel Spacing="{ThemeResource SettingsCardSpacing}">
|
||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" Text="{shcm:ResourceString Name=ViewPageSettingGachaLogHeader}"/>
|
||||
<TextBlock Style="{StaticResource SettingsCardHeaderTextBlockStyle}" Text="{shcm:ResourceString Name=ViewPageSettingGachaLogHeader}"/>
|
||||
<cwc:SettingsCard
|
||||
Description="{shcm:ResourceString Name=ViewPageSettingEmptyHistoryVisibleDescription}"
|
||||
Header="{shcm:ResourceString Name=ViewPageSettingEmptyHistoryVisibleHeader}"
|
||||
@@ -472,7 +472,7 @@
|
||||
<Border cw:Effects.Shadow="{ThemeResource CompatCardShadow}">
|
||||
<Border Padding="16" Style="{ThemeResource AcrylicBorderCardStyle}">
|
||||
<StackPanel Spacing="{ThemeResource SettingsCardSpacing}">
|
||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" Text="{shcm:ResourceString Name=ViewPageSettingStorageHeader}"/>
|
||||
<TextBlock Style="{StaticResource SettingsCardHeaderTextBlockStyle}" Text="{shcm:ResourceString Name=ViewPageSettingStorageHeader}"/>
|
||||
<cwc:SettingsExpander
|
||||
Description="{Binding DataFolderView.Size}"
|
||||
Header="{shcm:ResourceString Name=ViewPageSettingDataFolderHeader}"
|
||||
@@ -510,6 +510,11 @@
|
||||
Style="{ThemeResource SettingButtonStyle}"/>
|
||||
</cwc:SettingsExpander.Content>
|
||||
<cwc:SettingsExpander.Items>
|
||||
<cwc:SettingsCard
|
||||
Command="{Binding OpenBackgroundImageFolderCommand}"
|
||||
Description="{shcm:ResourceString Name=ViewPageSettingOpenBackgroundImageFolderDescription}"
|
||||
Header="{shcm:ResourceString Name=ViewPageSettingOpenBackgroundImageFolderHeader}"
|
||||
IsClickEnabled="True"/>
|
||||
<cwc:SettingsCard
|
||||
ActionIcon="{shcm:FontIcon Glyph=}"
|
||||
ActionIconToolTip="{shcm:ResourceString Name=ViewPageSettingResetAction}"
|
||||
|
||||
@@ -272,6 +272,12 @@ internal sealed partial class SettingViewModel : Abstraction.ViewModel
|
||||
}
|
||||
}
|
||||
|
||||
[Command("OpenBackgroundImageFolderCommand")]
|
||||
private async Task OpenBackgroundImageFolderAsync()
|
||||
{
|
||||
await Launcher.LaunchFolderPathAsync(runtimeOptions.GetDataFolderBackgroundFolder());
|
||||
}
|
||||
|
||||
[Command("DeleteUsersCommand")]
|
||||
private async Task DangerousDeleteUsersAsync()
|
||||
{
|
||||
|
||||
@@ -385,8 +385,12 @@ internal static class ApiEndpoints
|
||||
return $"{SdkStaticLauncherApi}/resource?key={scheme.Key}&launcher_id={scheme.LauncherId}&channel_id={scheme.Channel:D}&sub_channel_id={scheme.SubChannel:D}";
|
||||
}
|
||||
|
||||
// https://sdk-static.mihoyo.com/hk4e_cn/mdk/launcher/api/content?filter_adv=true&key=eYd89JmJ&language=zh-cn&launcher_id=18
|
||||
// https://sdk-static.mihoyo.com/hk4e_cn/mdk/launcher/api/content?key=eYd89JmJ&language=zh-cn&launcher_id=18
|
||||
public static string SdkStaticLauncherContent(LaunchScheme scheme, string languageCode, bool advOnly = true)
|
||||
{
|
||||
return advOnly
|
||||
? $"{SdkStaticLauncherApi}/content?filter_adv=true&key={scheme.Key}&launcher_id={scheme.LauncherId}&launguage={languageCode}"
|
||||
: $"{SdkStaticLauncherApi}/content?key={scheme.Key}&launcher_id={scheme.LauncherId}&launguage={languageCode}";
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Hosts | Queries
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<assemblyIdentity version="1.0.0.0" name="Snap.Hutao.app"/>
|
||||
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
|
||||
</windowsSettings>
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3" xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
|
||||
<windowsSettings xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
|
||||
<ws2:dpiAwareness>PerMonitorV2</ws2:dpiAwareness>
|
||||
<ws2:longPathAware>true</ws2:longPathAware>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
</assembly>
|
||||
</assembly>
|
||||
Reference in New Issue
Block a user