mirror of
https://github.com/AtomUI/AtomUI.git
synced 2026-10-18 22:46:38 +08:00
123 lines
3.7 KiB
C#
123 lines
3.7 KiB
C#
using Avalonia;
|
|
using Avalonia.Controls;
|
|
using Avalonia.Threading;
|
|
using System.Buffers.Binary;
|
|
using System.Diagnostics;
|
|
using System.IO.Compression;
|
|
using System.Text;
|
|
|
|
namespace AtomUI.Controls.Tests.ImageLoading;
|
|
|
|
internal sealed class ImageControlTestHost : IDisposable
|
|
{
|
|
private readonly Window _window;
|
|
|
|
internal ImageControlTestHost(Control control, double width = 160, double height = 120)
|
|
{
|
|
var arrangedWidth = double.IsFinite(control.Width) ? control.Width : width;
|
|
var arrangedHeight = double.IsFinite(control.Height) ? control.Height : height;
|
|
control.Measure(new Size(arrangedWidth, arrangedHeight));
|
|
control.Arrange(new Rect(0, 0, arrangedWidth, arrangedHeight));
|
|
_window = new Window
|
|
{
|
|
Width = width,
|
|
Height = height,
|
|
Content = control
|
|
};
|
|
_window.Show();
|
|
Dispatcher.UIThread.RunJobs();
|
|
}
|
|
|
|
internal void Detach()
|
|
{
|
|
_window.Content = null;
|
|
Dispatcher.UIThread.RunJobs();
|
|
}
|
|
|
|
internal void Attach(Control control)
|
|
{
|
|
_window.Content = control;
|
|
Dispatcher.UIThread.RunJobs();
|
|
}
|
|
|
|
internal static void WaitUntil(Func<bool> predicate, string description)
|
|
{
|
|
var timeout = Stopwatch.StartNew();
|
|
while (!predicate())
|
|
{
|
|
Dispatcher.UIThread.RunJobs();
|
|
if (timeout.Elapsed >= TimeSpan.FromSeconds(5))
|
|
{
|
|
throw new TimeoutException($"Timed out waiting for {description}.");
|
|
}
|
|
Thread.Yield();
|
|
}
|
|
Dispatcher.UIThread.RunJobs();
|
|
}
|
|
|
|
internal static byte[] CreatePng(int width, int height)
|
|
{
|
|
using var output = new MemoryStream();
|
|
output.Write([0x89, (byte)'P', (byte)'N', (byte)'G', 0x0d, 0x0a, 0x1a, 0x0a]);
|
|
|
|
Span<byte> header = stackalloc byte[13];
|
|
BinaryPrimitives.WriteUInt32BigEndian(header[..4], checked((uint)width));
|
|
BinaryPrimitives.WriteUInt32BigEndian(header[4..8], checked((uint)height));
|
|
header[8] = 8;
|
|
header[9] = 6;
|
|
WriteChunk(output, "IHDR", header);
|
|
|
|
using var raw = new MemoryStream();
|
|
var row = new byte[checked(width * 4 + 1)];
|
|
for (var y = 0; y < height; y++)
|
|
{
|
|
raw.Write(row);
|
|
}
|
|
raw.Position = 0;
|
|
using var compressed = new MemoryStream();
|
|
using (var zlib = new ZLibStream(compressed, CompressionLevel.SmallestSize, leaveOpen: true))
|
|
{
|
|
raw.CopyTo(zlib);
|
|
}
|
|
WriteChunk(output, "IDAT", compressed.ToArray());
|
|
WriteChunk(output, "IEND", []);
|
|
return output.ToArray();
|
|
}
|
|
|
|
private static void WriteChunk(Stream output, string type, ReadOnlySpan<byte> data)
|
|
{
|
|
Span<byte> length = stackalloc byte[4];
|
|
BinaryPrimitives.WriteUInt32BigEndian(length, checked((uint)data.Length));
|
|
output.Write(length);
|
|
var typeBytes = Encoding.ASCII.GetBytes(type);
|
|
output.Write(typeBytes);
|
|
output.Write(data);
|
|
|
|
var crcBytes = new byte[typeBytes.Length + data.Length];
|
|
typeBytes.CopyTo(crcBytes, 0);
|
|
data.CopyTo(crcBytes.AsSpan(typeBytes.Length));
|
|
Span<byte> crc = stackalloc byte[4];
|
|
BinaryPrimitives.WriteUInt32BigEndian(crc, CalculateCrc32(crcBytes));
|
|
output.Write(crc);
|
|
}
|
|
|
|
private static uint CalculateCrc32(ReadOnlySpan<byte> bytes)
|
|
{
|
|
var crc = uint.MaxValue;
|
|
foreach (var value in bytes)
|
|
{
|
|
crc ^= value;
|
|
for (var bit = 0; bit < 8; bit++)
|
|
{
|
|
crc = (crc & 1) != 0 ? 0xedb88320u ^ (crc >> 1) : crc >> 1;
|
|
}
|
|
}
|
|
return ~crc;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_window.Close();
|
|
}
|
|
}
|