Recovered from decompiled OpenMaui.Controls.Linux.dll: - SkiaShell.cs: FlyoutHeader, FlyoutFooter, scroll support (918 -> 1325 lines) - X11Window.cs: Cursor support (XCreateFontCursor, XDefineCursor) - All handlers with dark mode support - All services with latest implementations - LinuxApplication with theme change handling
554 lines
17 KiB
C#
554 lines
17 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Net.Http;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using SkiaSharp;
|
|
using Svg.Skia;
|
|
|
|
namespace Microsoft.Maui.Platform;
|
|
|
|
public class SkiaImage : SkiaView
|
|
{
|
|
private SKBitmap? _bitmap;
|
|
|
|
private SKImage? _image;
|
|
|
|
private bool _isLoading;
|
|
|
|
private string? _currentFilePath;
|
|
|
|
private bool _isSvg;
|
|
|
|
private CancellationTokenSource? _loadCts;
|
|
|
|
private readonly object _loadLock = new object();
|
|
|
|
private double _svgLoadedWidth;
|
|
|
|
private double _svgLoadedHeight;
|
|
|
|
private bool _pendingSvgReload;
|
|
|
|
private SKRect _lastArrangedBounds;
|
|
|
|
public SKBitmap? Bitmap
|
|
{
|
|
get
|
|
{
|
|
return _bitmap;
|
|
}
|
|
set
|
|
{
|
|
SKBitmap? bitmap = _bitmap;
|
|
if (bitmap != null)
|
|
{
|
|
((SKNativeObject)bitmap).Dispose();
|
|
}
|
|
_bitmap = value;
|
|
SKImage? image = _image;
|
|
if (image != null)
|
|
{
|
|
((SKNativeObject)image).Dispose();
|
|
}
|
|
_image = ((value != null) ? SKImage.FromBitmap(value) : null);
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
public Aspect Aspect { get; set; }
|
|
|
|
public bool IsOpaque { get; set; }
|
|
|
|
public bool IsLoading => _isLoading;
|
|
|
|
public bool IsAnimationPlaying { get; set; }
|
|
|
|
public new double WidthRequest
|
|
{
|
|
get
|
|
{
|
|
return base.WidthRequest;
|
|
}
|
|
set
|
|
{
|
|
base.WidthRequest = value;
|
|
ScheduleSvgReloadIfNeeded();
|
|
}
|
|
}
|
|
|
|
public new double HeightRequest
|
|
{
|
|
get
|
|
{
|
|
return base.HeightRequest;
|
|
}
|
|
set
|
|
{
|
|
base.HeightRequest = value;
|
|
ScheduleSvgReloadIfNeeded();
|
|
}
|
|
}
|
|
|
|
public event EventHandler? ImageLoaded;
|
|
|
|
public event EventHandler<ImageLoadingErrorEventArgs>? ImageLoadingError;
|
|
|
|
private void ScheduleSvgReloadIfNeeded()
|
|
{
|
|
if (_isSvg && !string.IsNullOrEmpty(_currentFilePath))
|
|
{
|
|
double widthRequest = WidthRequest;
|
|
double heightRequest = HeightRequest;
|
|
if (widthRequest > 0.0 && heightRequest > 0.0 && (Math.Abs(_svgLoadedWidth - widthRequest) > 0.5 || Math.Abs(_svgLoadedHeight - heightRequest) > 0.5) && !_pendingSvgReload)
|
|
{
|
|
_pendingSvgReload = true;
|
|
ReloadSvgDebounced();
|
|
}
|
|
}
|
|
}
|
|
|
|
private async Task ReloadSvgDebounced()
|
|
{
|
|
await Task.Delay(10);
|
|
_pendingSvgReload = false;
|
|
if (!string.IsNullOrEmpty(_currentFilePath) && WidthRequest > 0.0 && HeightRequest > 0.0)
|
|
{
|
|
Console.WriteLine($"[SkiaImage] Reloading SVG at {WidthRequest}x{HeightRequest} (was {_svgLoadedWidth}x{_svgLoadedHeight})");
|
|
await LoadSvgAtSizeAsync(_currentFilePath, WidthRequest, HeightRequest);
|
|
}
|
|
}
|
|
|
|
protected override void OnDraw(SKCanvas canvas, SKRect bounds)
|
|
{
|
|
//IL_0009: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_000e: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_001a: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_001f: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0021: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_002b: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0034: Expected O, but got Unknown
|
|
//IL_0035: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0076: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_007b: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0080: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0081: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0086: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_008d: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0095: Expected O, but got Unknown
|
|
//IL_009c: Unknown result type (might be due to invalid IL or missing references)
|
|
if (!IsOpaque && base.BackgroundColor != SKColors.Transparent)
|
|
{
|
|
SKPaint val = new SKPaint
|
|
{
|
|
Color = base.BackgroundColor,
|
|
Style = (SKPaintStyle)0
|
|
};
|
|
try
|
|
{
|
|
canvas.DrawRect(bounds, val);
|
|
}
|
|
finally
|
|
{
|
|
((IDisposable)val)?.Dispose();
|
|
}
|
|
}
|
|
if (_image == null)
|
|
{
|
|
return;
|
|
}
|
|
int width = _image.Width;
|
|
int height = _image.Height;
|
|
if (width <= 0 || height <= 0)
|
|
{
|
|
return;
|
|
}
|
|
SKRect val2 = CalculateDestRect(bounds, width, height);
|
|
SKPaint val3 = new SKPaint
|
|
{
|
|
IsAntialias = true,
|
|
FilterQuality = (SKFilterQuality)3
|
|
};
|
|
try
|
|
{
|
|
canvas.DrawImage(_image, val2, val3);
|
|
}
|
|
finally
|
|
{
|
|
((IDisposable)val3)?.Dispose();
|
|
}
|
|
}
|
|
|
|
private SKRect CalculateDestRect(SKRect bounds, float imageWidth, float imageHeight)
|
|
{
|
|
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_001f: Expected I4, but got Unknown
|
|
//IL_0081: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0120: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0126: Unknown result type (might be due to invalid IL or missing references)
|
|
Aspect aspect = Aspect;
|
|
switch ((int)aspect)
|
|
{
|
|
case 2:
|
|
return bounds;
|
|
case 0:
|
|
{
|
|
float num6 = Math.Min(((SKRect)(ref bounds)).Width / imageWidth, ((SKRect)(ref bounds)).Height / imageHeight);
|
|
float num4 = imageWidth * num6;
|
|
float num5 = imageHeight * num6;
|
|
float num = ((SKRect)(ref bounds)).Left + (((SKRect)(ref bounds)).Width - num4) / 2f;
|
|
float num2 = ((SKRect)(ref bounds)).Top + (((SKRect)(ref bounds)).Height - num5) / 2f;
|
|
return new SKRect(num, num2, num + num4, num2 + num5);
|
|
}
|
|
case 1:
|
|
{
|
|
float num3 = Math.Max(((SKRect)(ref bounds)).Width / imageWidth, ((SKRect)(ref bounds)).Height / imageHeight);
|
|
float num4 = imageWidth * num3;
|
|
float num5 = imageHeight * num3;
|
|
float num = ((SKRect)(ref bounds)).Left + (((SKRect)(ref bounds)).Width - num4) / 2f;
|
|
float num2 = ((SKRect)(ref bounds)).Top + (((SKRect)(ref bounds)).Height - num5) / 2f;
|
|
return new SKRect(num, num2, num + num4, num2 + num5);
|
|
}
|
|
case 3:
|
|
{
|
|
float num = ((SKRect)(ref bounds)).Left + (((SKRect)(ref bounds)).Width - imageWidth) / 2f;
|
|
float num2 = ((SKRect)(ref bounds)).Top + (((SKRect)(ref bounds)).Height - imageHeight) / 2f;
|
|
return new SKRect(num, num2, num + imageWidth, num2 + imageHeight);
|
|
}
|
|
default:
|
|
return bounds;
|
|
}
|
|
}
|
|
|
|
public async Task LoadFromFileAsync(string filePath)
|
|
{
|
|
_isLoading = true;
|
|
Invalidate();
|
|
Console.WriteLine($"[SkiaImage] LoadFromFileAsync: {filePath}, WidthRequest={WidthRequest}, HeightRequest={HeightRequest}");
|
|
try
|
|
{
|
|
List<string> list = new List<string>
|
|
{
|
|
filePath,
|
|
Path.Combine(AppContext.BaseDirectory, filePath),
|
|
Path.Combine(AppContext.BaseDirectory, "Resources", "Images", filePath),
|
|
Path.Combine(AppContext.BaseDirectory, "Resources", filePath)
|
|
};
|
|
if (filePath.EndsWith(".png", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
string text = Path.ChangeExtension(filePath, ".svg");
|
|
list.Add(text);
|
|
list.Add(Path.Combine(AppContext.BaseDirectory, text));
|
|
list.Add(Path.Combine(AppContext.BaseDirectory, "Resources", "Images", text));
|
|
list.Add(Path.Combine(AppContext.BaseDirectory, "Resources", text));
|
|
}
|
|
string foundPath = null;
|
|
foreach (string item in list)
|
|
{
|
|
if (File.Exists(item))
|
|
{
|
|
foundPath = item;
|
|
Console.WriteLine("[SkiaImage] Found file at: " + item);
|
|
break;
|
|
}
|
|
}
|
|
if (foundPath == null)
|
|
{
|
|
Console.WriteLine("[SkiaImage] File not found: " + filePath);
|
|
_isLoading = false;
|
|
_isSvg = false;
|
|
_currentFilePath = null;
|
|
this.ImageLoadingError?.Invoke(this, new ImageLoadingErrorEventArgs(new FileNotFoundException(filePath)));
|
|
return;
|
|
}
|
|
_isSvg = foundPath.EndsWith(".svg", StringComparison.OrdinalIgnoreCase);
|
|
_currentFilePath = foundPath;
|
|
if (!_isSvg)
|
|
{
|
|
await Task.Run(delegate
|
|
{
|
|
using FileStream fileStream = File.OpenRead(foundPath);
|
|
SKBitmap val = SKBitmap.Decode((Stream)fileStream);
|
|
if (val != null)
|
|
{
|
|
Bitmap = val;
|
|
Console.WriteLine("[SkiaImage] Loaded image: " + foundPath);
|
|
}
|
|
});
|
|
}
|
|
else
|
|
{
|
|
await LoadSvgAtSizeAsync(foundPath, WidthRequest, HeightRequest);
|
|
}
|
|
_isLoading = false;
|
|
this.ImageLoaded?.Invoke(this, EventArgs.Empty);
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
_isLoading = false;
|
|
this.ImageLoadingError?.Invoke(this, new ImageLoadingErrorEventArgs(exception));
|
|
}
|
|
Invalidate();
|
|
}
|
|
|
|
private async Task LoadSvgAtSizeAsync(string svgPath, double targetWidth, double targetHeight)
|
|
{
|
|
_loadCts?.Cancel();
|
|
CancellationTokenSource cts = new CancellationTokenSource();
|
|
_loadCts = cts;
|
|
try
|
|
{
|
|
SKBitmap newBitmap = null;
|
|
await Task.Run(delegate
|
|
{
|
|
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_001c: Expected O, but got Unknown
|
|
//IL_0052: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0057: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0109: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0113: Expected O, but got Unknown
|
|
//IL_0119: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0120: Expected O, but got Unknown
|
|
//IL_0122: Unknown result type (might be due to invalid IL or missing references)
|
|
if (cts.Token.IsCancellationRequested)
|
|
{
|
|
return;
|
|
}
|
|
SKSvg val = new SKSvg();
|
|
try
|
|
{
|
|
val.Load(svgPath);
|
|
if (val.Picture != null && !cts.Token.IsCancellationRequested)
|
|
{
|
|
SKRect cullRect = val.Picture.CullRect;
|
|
float num = ((targetWidth > 0.0) ? ((float)targetWidth) : ((((SKRect)(ref cullRect)).Width <= 24f) ? 24f : ((SKRect)(ref cullRect)).Width));
|
|
float num2 = Math.Min(val2: ((targetHeight > 0.0) ? ((float)targetHeight) : ((((SKRect)(ref cullRect)).Height <= 24f) ? 24f : ((SKRect)(ref cullRect)).Height)) / ((SKRect)(ref cullRect)).Height, val1: num / ((SKRect)(ref cullRect)).Width);
|
|
int num3 = Math.Max(1, (int)(((SKRect)(ref cullRect)).Width * num2));
|
|
int num4 = Math.Max(1, (int)(((SKRect)(ref cullRect)).Height * num2));
|
|
newBitmap = new SKBitmap(num3, num4, false);
|
|
SKCanvas val2 = new SKCanvas(newBitmap);
|
|
try
|
|
{
|
|
val2.Clear(SKColors.Transparent);
|
|
val2.Scale(num2);
|
|
val2.DrawPicture(val.Picture, (SKPaint)null);
|
|
Console.WriteLine($"[SkiaImage] Loaded SVG: {svgPath} at {num3}x{num4} (requested {targetWidth}x{targetHeight})");
|
|
return;
|
|
}
|
|
finally
|
|
{
|
|
((IDisposable)val2)?.Dispose();
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
((IDisposable)val)?.Dispose();
|
|
}
|
|
}, cts.Token);
|
|
if (!cts.Token.IsCancellationRequested && newBitmap != null)
|
|
{
|
|
_svgLoadedWidth = ((targetWidth > 0.0) ? targetWidth : ((double)newBitmap.Width));
|
|
_svgLoadedHeight = ((targetHeight > 0.0) ? targetHeight : ((double)newBitmap.Height));
|
|
Bitmap = newBitmap;
|
|
return;
|
|
}
|
|
SKBitmap obj = newBitmap;
|
|
if (obj != null)
|
|
{
|
|
((SKNativeObject)obj).Dispose();
|
|
}
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
}
|
|
}
|
|
|
|
public async Task LoadFromStreamAsync(Stream stream)
|
|
{
|
|
_isLoading = true;
|
|
Invalidate();
|
|
try
|
|
{
|
|
await Task.Run(delegate
|
|
{
|
|
SKBitmap val = SKBitmap.Decode(stream);
|
|
if (val != null)
|
|
{
|
|
Bitmap = val;
|
|
}
|
|
});
|
|
_isLoading = false;
|
|
this.ImageLoaded?.Invoke(this, EventArgs.Empty);
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
_isLoading = false;
|
|
this.ImageLoadingError?.Invoke(this, new ImageLoadingErrorEventArgs(exception));
|
|
}
|
|
Invalidate();
|
|
}
|
|
|
|
public async Task LoadFromUriAsync(Uri uri)
|
|
{
|
|
_isLoading = true;
|
|
Invalidate();
|
|
try
|
|
{
|
|
using HttpClient httpClient = new HttpClient();
|
|
using MemoryStream memoryStream = new MemoryStream(await httpClient.GetByteArrayAsync(uri));
|
|
SKBitmap val = SKBitmap.Decode((Stream)memoryStream);
|
|
if (val != null)
|
|
{
|
|
Bitmap = val;
|
|
}
|
|
_isLoading = false;
|
|
this.ImageLoaded?.Invoke(this, EventArgs.Empty);
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
_isLoading = false;
|
|
this.ImageLoadingError?.Invoke(this, new ImageLoadingErrorEventArgs(exception));
|
|
}
|
|
Invalidate();
|
|
}
|
|
|
|
public void LoadFromData(byte[] data)
|
|
{
|
|
try
|
|
{
|
|
using MemoryStream memoryStream = new MemoryStream(data);
|
|
SKBitmap val = SKBitmap.Decode((Stream)memoryStream);
|
|
if (val != null)
|
|
{
|
|
Bitmap = val;
|
|
}
|
|
this.ImageLoaded?.Invoke(this, EventArgs.Empty);
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
this.ImageLoadingError?.Invoke(this, new ImageLoadingErrorEventArgs(exception));
|
|
}
|
|
}
|
|
|
|
public void LoadFromBitmap(SKBitmap bitmap)
|
|
{
|
|
try
|
|
{
|
|
_isSvg = false;
|
|
_currentFilePath = null;
|
|
Bitmap = bitmap;
|
|
_isLoading = false;
|
|
this.ImageLoaded?.Invoke(this, EventArgs.Empty);
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
_isLoading = false;
|
|
this.ImageLoadingError?.Invoke(this, new ImageLoadingErrorEventArgs(exception));
|
|
}
|
|
Invalidate();
|
|
}
|
|
|
|
public override void Arrange(SKRect bounds)
|
|
{
|
|
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
|
|
base.Arrange(bounds);
|
|
if ((!(base.WidthRequest > 0.0) || !(base.HeightRequest > 0.0)) && _isSvg && !string.IsNullOrEmpty(_currentFilePath) && !_isLoading)
|
|
{
|
|
float width = ((SKRect)(ref bounds)).Width;
|
|
float height = ((SKRect)(ref bounds)).Height;
|
|
if (((double)width > _svgLoadedWidth * 1.1 || (double)height > _svgLoadedHeight * 1.1) && width > 0f && height > 0f && (width != ((SKRect)(ref _lastArrangedBounds)).Width || height != ((SKRect)(ref _lastArrangedBounds)).Height))
|
|
{
|
|
_lastArrangedBounds = bounds;
|
|
Console.WriteLine($"[SkiaImage] Arrange detected larger bounds: {width}x{height} vs loaded {_svgLoadedWidth}x{_svgLoadedHeight}");
|
|
LoadSvgAtSizeAsync(_currentFilePath, width, height);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected override SKSize MeasureOverride(SKSize availableSize)
|
|
{
|
|
//IL_002a: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0048: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_006e: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_005e: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0131: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0107: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0163: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_015b: Unknown result type (might be due to invalid IL or missing references)
|
|
double widthRequest = base.WidthRequest;
|
|
double heightRequest = base.HeightRequest;
|
|
if (widthRequest > 0.0 && heightRequest > 0.0)
|
|
{
|
|
return new SKSize((float)widthRequest, (float)heightRequest);
|
|
}
|
|
if (_image == null)
|
|
{
|
|
if (widthRequest > 0.0)
|
|
{
|
|
return new SKSize((float)widthRequest, (float)widthRequest);
|
|
}
|
|
if (heightRequest > 0.0)
|
|
{
|
|
return new SKSize((float)heightRequest, (float)heightRequest);
|
|
}
|
|
return new SKSize(100f, 100f);
|
|
}
|
|
float num = _image.Width;
|
|
float num2 = _image.Height;
|
|
if (widthRequest > 0.0)
|
|
{
|
|
float num3 = (float)widthRequest / num;
|
|
return new SKSize((float)widthRequest, num2 * num3);
|
|
}
|
|
if (heightRequest > 0.0)
|
|
{
|
|
float num4 = (float)heightRequest / num2;
|
|
return new SKSize(num * num4, (float)heightRequest);
|
|
}
|
|
if (((SKSize)(ref availableSize)).Width < float.MaxValue && ((SKSize)(ref availableSize)).Height < float.MaxValue)
|
|
{
|
|
float num5 = Math.Min(((SKSize)(ref availableSize)).Width / num, ((SKSize)(ref availableSize)).Height / num2);
|
|
return new SKSize(num * num5, num2 * num5);
|
|
}
|
|
if (((SKSize)(ref availableSize)).Width < float.MaxValue)
|
|
{
|
|
float num6 = ((SKSize)(ref availableSize)).Width / num;
|
|
return new SKSize(((SKSize)(ref availableSize)).Width, num2 * num6);
|
|
}
|
|
if (((SKSize)(ref availableSize)).Height < float.MaxValue)
|
|
{
|
|
float num7 = ((SKSize)(ref availableSize)).Height / num2;
|
|
return new SKSize(num * num7, ((SKSize)(ref availableSize)).Height);
|
|
}
|
|
return new SKSize(num, num2);
|
|
}
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
if (disposing)
|
|
{
|
|
SKBitmap? bitmap = _bitmap;
|
|
if (bitmap != null)
|
|
{
|
|
((SKNativeObject)bitmap).Dispose();
|
|
}
|
|
SKImage? image = _image;
|
|
if (image != null)
|
|
{
|
|
((SKNativeObject)image).Dispose();
|
|
}
|
|
}
|
|
base.Dispose(disposing);
|
|
}
|
|
}
|