using Harmonia.Core.Caching; using Harmonia.Core.Imaging; using Harmonia.Core.Models; using Microsoft.Extensions.Caching.Memory; using Microsoft.UI.Dispatching; using Microsoft.UI.Xaml.Media.Imaging; using System; using System.Collections.Concurrent; using System.IO; using System.Threading; using System.Threading.Tasks; namespace Harmonia.WinUI.Caching; public class AudioBitmapImageCache(IAudioImageExtractor audioImageExtractor) : MemoryCache, IAudioBitmapImageCache { protected override MemoryCacheOptions Options => new() { SizeLimit = 200_000_000, CompactionPercentage = 0.2, }; protected override TimeSpan SlidingExpiration => TimeSpan.FromSeconds(600); protected override int MaxConcurrentRequests => 8; protected virtual int MaxImageWidthOrHeight => 1000; protected override object? GetKey(Song key) { if (string.IsNullOrWhiteSpace(key.ImageHash) == false) { return key.ImageHash; } else if (string.IsNullOrWhiteSpace(key.ImageName) == false) { return key.ImageName; } return "Default"; } protected override async ValueTask FetchAsync(Song key, CancellationToken cancellationToken) { SongPictureInfo? songPictureInfo = await audioImageExtractor.ExtractImageAsync(key.FileName, cancellationToken); if (songPictureInfo == null) return GetDefaultBitmapImage(); using MemoryStream stream = new(songPictureInfo.Data); BitmapImage bitmapImage = new(); await bitmapImage.SetSourceAsync(stream.AsRandomAccessStream()); bitmapImage.DecodePixelWidth = GetDecodePixelWidth(bitmapImage); bitmapImage.DecodePixelHeight = GetDecodePixelHeight(bitmapImage); return bitmapImage; } private BitmapImage GetDefaultBitmapImage() { Uri uri = new("ms-appx:///Assets/Default.png", UriKind.Absolute); BitmapImage bitmapImage = new(); bitmapImage.DecodePixelWidth = GetDecodePixelWidth(bitmapImage); bitmapImage.DecodePixelHeight = GetDecodePixelHeight(bitmapImage); bitmapImage.UriSource = uri; return bitmapImage; } private int GetDecodePixelWidth(BitmapImage bitmapImage) { int originalImageWidth = bitmapImage.PixelWidth; int orignalImageHeight = bitmapImage.PixelHeight; if (originalImageWidth <= MaxImageWidthOrHeight && orignalImageHeight <= MaxImageWidthOrHeight) return 0; if (orignalImageHeight > originalImageWidth) return 0; return MaxImageWidthOrHeight; } private int GetDecodePixelHeight(BitmapImage bitmapImage) { int originalImageWidth = bitmapImage.PixelWidth; int orignalImageHeight = bitmapImage.PixelHeight; if (originalImageWidth <= MaxImageWidthOrHeight && orignalImageHeight <= MaxImageWidthOrHeight) return 0; if (originalImageWidth > orignalImageHeight) return 0; return MaxImageWidthOrHeight; } protected override long GetEntrySize(BitmapImage entry) { return entry.DecodePixelWidth * entry.DecodePixelHeight; } } public class AudioBitmapImageCache2 : MemoryCache, IAudioBitmapImageCache { private readonly IAudioImageExtractor _audioImageExtractor; private readonly BitmapImage[] _bitmapPool; private int _nextIndex = 0; private ConcurrentDictionary _test = []; protected override MemoryCacheOptions Options => new() { SizeLimit = 200_000_000, CompactionPercentage = 0.2, }; protected override TimeSpan SlidingExpiration => TimeSpan.FromSeconds(600); protected override int MaxConcurrentRequests => 8; protected virtual int MaxImageWidthOrHeight => 1000; protected virtual int BitmapPoolSize => 64; public AudioBitmapImageCache2(IAudioImageExtractor audioImageExtractor) { _audioImageExtractor = audioImageExtractor; _bitmapPool = new BitmapImage[BitmapPoolSize]; InitializeBitmapPool(); } private void InitializeBitmapPool() { for (int i = 0; i < _bitmapPool.Length; i++) _bitmapPool[i] = new BitmapImage(); //DispatcherQueue dispatcherQueue = DispatcherQueue.GetForCurrentThread(); //TaskCompletionSource taskCompletionSource = new(); //dispatcherQueue.TryEnqueue(() => //{ // for (int i = 0; i < _bitmapPool.Length; i++) // _bitmapPool[i] = new BitmapImage(); // taskCompletionSource.SetResult(); //}); //taskCompletionSource.Task.GetAwaiter().GetResult(); } protected override object? GetKey(Song key) { if (string.IsNullOrWhiteSpace(key.ImageHash) == false) { return key.ImageHash; } else if (string.IsNullOrWhiteSpace(key.ImageName) == false) { return key.ImageName; } return "Default"; } protected override async ValueTask FetchAsync(Song key, CancellationToken cancellationToken) { int index = Interlocked.Increment(ref _nextIndex); BitmapImage bitmapImage = _bitmapPool[index % _bitmapPool.Length]; //_test.AddOrUpdate(index, bitmapImage); SongPictureInfo? songPictureInfo = await _audioImageExtractor.ExtractImageAsync(key.FileName, cancellationToken); if (songPictureInfo == null) { bitmapImage.UriSource = new("ms-appx:///Assets/Default.png", UriKind.Absolute); } else { using MemoryStream stream = new(songPictureInfo.Data); await bitmapImage.SetSourceAsync(stream.AsRandomAccessStream()); } bitmapImage.DecodePixelWidth = GetDecodePixelWidth(bitmapImage); bitmapImage.DecodePixelHeight = GetDecodePixelHeight(bitmapImage); return bitmapImage; } private int GetDecodePixelWidth(BitmapImage bitmapImage) { int originalImageWidth = bitmapImage.PixelWidth; int orignalImageHeight = bitmapImage.PixelHeight; if (originalImageWidth <= MaxImageWidthOrHeight && orignalImageHeight <= MaxImageWidthOrHeight) return 0; if (orignalImageHeight > originalImageWidth) return 0; return MaxImageWidthOrHeight; } private int GetDecodePixelHeight(BitmapImage bitmapImage) { int originalImageWidth = bitmapImage.PixelWidth; int orignalImageHeight = bitmapImage.PixelHeight; if (originalImageWidth <= MaxImageWidthOrHeight && orignalImageHeight <= MaxImageWidthOrHeight) return 0; if (originalImageWidth > orignalImageHeight) return 0; return MaxImageWidthOrHeight; } protected override long GetEntrySize(BitmapImage entry) { return entry.DecodePixelWidth * entry.DecodePixelHeight; } protected override void PostEvictionCallback(object? cacheKey, object? cacheValue, EvictionReason evictionReason, object? state) { } }