using System.IO.Pipelines; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; using StitchATon2.Infra; using StitchATon2.Infra.Buffers; using StitchATon2.Infra.Encoders; using StitchATon2.Infra.Synchronization; namespace StitchATon2.Domain.ImageCreators; public sealed class DangerousImageCreator : IDisposable { private readonly GridSection _section; private readonly IBuffer _mmfReadBuffer; private int FullWidth => _section.TileManager.Configuration.FullWidth; private int FullHeight => _section.TileManager.Configuration.FullHeight; private int OffsetX => _section.OffsetX; private int OffsetY => _section.OffsetY; private int Width => _section.Width; private int Height => _section.Height; private int TileWidth => _section.TileManager.Configuration.Width; private int TileHeight => _section.TileManager.Configuration.Height; private Tile TileOrigin => _section.Origin; private int RightmostPixelIndex => _section.TileManager.Configuration.RightTileIndex; private int BottomPixelIndex => _section.TileManager.Configuration.BottomTileIndex; private TileManager TileManager => _section.TileManager; public DangerousImageCreator(GridSection section) { _section = section; _mmfReadBuffer = MemoryAllocator.Allocate(TileWidth); } ~DangerousImageCreator() => Dispose(); public async Task WriteToPipe(PipeWriter outputPipe, float scale, CancellationToken cancellationToken = default) { var scaleFactor = MathF.ReciprocalEstimate(scale); var targetWidth = (int)(Width / scaleFactor); var targetHeight = (int)(Height / scaleFactor); var encoder = new PngPipeEncoder(outputPipe, targetWidth, targetHeight); encoder.WriteHeader(); var outputBufferSize = targetWidth * Unsafe.SizeOf(); using var xLookup = Utils.BoundsMatrix(scaleFactor, targetWidth, FullWidth, OffsetX); using var yLookup = Utils.BoundsMatrix(scaleFactor, targetHeight, FullHeight, OffsetY); using var yStartMap = MemoryAllocator.Allocate(targetWidth); using var yEndMap = MemoryAllocator.Allocate(targetWidth); var yStart = OffsetY; var outputTaskQueue = TaskHelper.SynchronizedTaskFactory.StartNew(() => { }, cancellationToken); for (var y = 0; y < targetHeight; y++) { var yEnd = yLookup[y]; var (localRow0, localOffsetY0) = int.DivRem(yStart, TileHeight); MapRow(localRow0, localOffsetY0, xLookup, targetWidth, yStartMap); var (localRow1, localOffsetY1) = int.DivRem(yEnd, TileHeight); MapRow(localRow1, localOffsetY1, xLookup, targetWidth, yEndMap); if (localRow0 != localRow1) { MapRow(localRow0, BottomPixelIndex, xLookup, targetWidth, yEndMap, true); } int xStart = OffsetX, x0 = 0; var pxInt32 = Int32Pixel.Zero; ref var px = ref pxInt32; ref var rChannel = ref Unsafe.As(ref px); ref var gChannel = ref Unsafe.Add(ref rChannel, 4); ref var bChannel = ref Unsafe.Add(ref rChannel, 8); var outputBuffer = MemoryAllocator.Allocate(outputBufferSize); ref var outputChannel = ref outputBuffer.Span[0]; for (int x1 = 0; x1 < targetWidth; x1++) { var xEnd = xLookup[x1]; px = yEndMap[x1]; px += yStartMap[x0]; px -= yEndMap[x0]; px -= yStartMap[x1]; px /= Math.Max(1, (xEnd - xStart) * (yEnd - yStart)); outputChannel = rChannel; outputChannel = ref Unsafe.Add(ref outputChannel, 1); outputChannel = gChannel; outputChannel = ref Unsafe.Add(ref outputChannel, 1); outputChannel = bChannel; outputChannel = ref Unsafe.Add(ref outputChannel, 1); xStart = xEnd; x0 = x1; } outputTaskQueue = outputTaskQueue .ContinueWith(_ => encoder.WriteData(outputBuffer, cancellationToken: cancellationToken), cancellationToken); yStart = yEnd; } await outputTaskQueue; encoder.WriteEndOfFile(cancellationToken); } private void MapRow( int rowOffset, int yOffset, IBuffer boundsMatrix, int count, IBuffer destination, bool appendMode = false) { var sourceMap = boundsMatrix.Span[..count]; var currentTile = TileManager.GetAdjacent(TileOrigin, 0, rowOffset); var xAdder = Int32Pixel.Zero; var xOffset = 0; var written = 0; var destinationSpan = destination.Span; var readBufferSpan = _mmfReadBuffer.Span; while (true) { currentTile.Integral.Acquire(yOffset, readBufferSpan); int localX; if (appendMode) { while (written < sourceMap.Length && (localX = sourceMap[written] - xOffset) < TileWidth) { destinationSpan[written] += readBufferSpan[localX]; destinationSpan[written] += xAdder; written++; } } else { while (written < sourceMap.Length && (localX = sourceMap[written] - xOffset) < TileWidth) { destinationSpan[written] = readBufferSpan[localX]; destinationSpan[written] += xAdder; written++; } } if (written >= sourceMap.Length) break; xAdder += readBufferSpan[RightmostPixelIndex]; xOffset += TileWidth; currentTile = TileManager.GetAdjacent(currentTile, 1, 0); } } public void Dispose() { _mmfReadBuffer.Dispose(); GC.SuppressFinalize(this); } }