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 WriteToPipe2(PipeWriter outputPipe, float scale, CancellationToken cancellationToken = default) { var scaleFactor = MathF.ReciprocalEstimate(scale); var targetWidth = (int)(Width / scaleFactor); var targetHeight = (int)(Height / scaleFactor); if (targetHeight == 0 || targetWidth == 0) return; var encoder = new PngPipeEncoder(outputPipe, targetWidth, targetHeight); encoder.WriteHeader(); var outputBufferSize = targetWidth * Unsafe.SizeOf(); var xLookup = Utils.DoubleBoundsMatrix(scaleFactor, targetWidth, FullWidth, OffsetX); var yLookup = Utils.DoubleBoundsMatrix(scaleFactor, targetHeight, FullHeight, OffsetY); using var xLookup0 = xLookup.Item1; using var xLookup1 = xLookup.Item2; using var yLookup0 = yLookup.Item1; using var yLookup1 = yLookup.Item2; using var yStartMap0 = MemoryAllocator.Allocate(targetWidth); using var yStartMap1 = MemoryAllocator.Allocate(targetWidth); using var yEndMap0 = MemoryAllocator.Allocate(targetWidth); using var yEndMap1 = MemoryAllocator.Allocate(targetWidth); // var yStart = OffsetY; // Use pixel referencing to eliminate type casting 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 outputTaskQueue = TaskHelper.SynchronizedTaskFactory.StartNew(() => { }, cancellationToken); for (var y = 0; y < targetHeight; y++) { var yStart = yLookup0[y]; var yEnd = yLookup1[y]; var (localRow0, localOffsetY0) = int.DivRem(yStart, TileHeight); MapRow(localRow0, localOffsetY0, xLookup0, targetWidth, yStartMap0); MapRow(localRow0, localOffsetY0, xLookup1, targetWidth, yStartMap1); var (localRow1, localOffsetY1) = int.DivRem(yEnd, TileHeight); MapRow(localRow1, localOffsetY1, xLookup0, targetWidth, yEndMap0); MapRow(localRow1, localOffsetY1, xLookup1, targetWidth, yEndMap1); if (localRow0 != localRow1) { MapRow(localRow0, BottomPixelIndex, xLookup0, targetWidth, yEndMap0, true); MapRow(localRow0, BottomPixelIndex, xLookup1, targetWidth, yEndMap1, true); } // int xStart = OffsetX, x0 = 0; var outputBuffer = MemoryAllocator.Allocate(outputBufferSize); ref var outputChannel = ref outputBuffer.Span[0]; var boxHeight = Math.Max(1, yEnd - yStart); for (int x = 0; x < targetWidth; x++) { var xStart = xLookup0[x]; var xEnd = xLookup1[x]; px = yEndMap1[x]; px += yStartMap0[x]; px -= yEndMap0[x]; px -= yStartMap1[x]; px /= Math.Max(1, xEnd - xStart) * boxHeight; outputChannel = rChannel; outputChannel = ref Unsafe.AddByteOffset(ref outputChannel, 1); outputChannel = gChannel; outputChannel = ref Unsafe.AddByteOffset(ref outputChannel, 1); outputChannel = bChannel; outputChannel = ref Unsafe.AddByteOffset(ref outputChannel, 1); // xStart = xEnd; // x0 = x; } outputTaskQueue = outputTaskQueue .ContinueWith(async _ => { await encoder.WriteDataAsync(outputBuffer, cancellationToken: cancellationToken); }, cancellationToken); yStart = yEnd; } await outputTaskQueue; await encoder.WriteEndOfFileAsync(cancellationToken); } 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); if (targetHeight == 0 || targetWidth == 0) return; var encoder = new PngPipeEncoder(outputPipe, targetWidth, targetHeight); encoder.WriteHeader(); Task outputTaskQueue; 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 + 1); using var yEndMap = MemoryAllocator.Allocate(targetWidth + 1); // OffsetX-(int)float.Ceiling(scaleFactor) int yStart = OffsetY, yEnd = yLookup[0], xStart = OffsetX, x0 = 0; // Use pixel referencing to eliminate type casting 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); // First row var (localRow0, localOffsetY0) = int.DivRem(yStart, TileHeight); var (localRow1, localOffsetY1) = int.DivRem(yEnd, TileHeight); { switch (localOffsetY0) { // Cross row tile, no need to handle if it's first row tile for now // the provided asset is bordered black anyway case 0 when TileOrigin.Row > 1: localOffsetY0 = BottomPixelIndex; localRow0--; break; case > 0: localOffsetY0--; break; } MapRow(localRow0, localOffsetY0, xLookup, targetWidth, yStartMap); MapRow(localRow1, localOffsetY1, xLookup, targetWidth, yEndMap); // Cross row if (localRow0 != localRow1) MapRow(localRow0, BottomPixelIndex, xLookup, targetWidth, yEndMap, true); var outputBuffer = MemoryAllocator.Allocate(outputBufferSize); ref var outputChannel = ref outputBuffer.Span[0]; var boxHeight = yEnd - yStart; // Render first pixel row var xEnd = xLookup[0]; px = yEndMap[0]; px += yStartMap[^1]; px -= yEndMap[^1]; px -= yStartMap[0]; px /= Math.Max(1, (xEnd - xStart) * boxHeight); outputChannel = rChannel; outputChannel = ref Unsafe.AddByteOffset(ref outputChannel, 1); outputChannel = gChannel; outputChannel = ref Unsafe.AddByteOffset(ref outputChannel, 1); outputChannel = bChannel; outputChannel = ref Unsafe.AddByteOffset(ref outputChannel, 1); xStart = xEnd; // Render entire pixel row for (int x1 = 1; x1 < targetWidth; x1++) { xEnd = xLookup[x1]; px = yEndMap[x1]; px += yStartMap[x0]; px -= yEndMap[x0]; px -= yStartMap[x1]; px /= Math.Max(1, (xEnd - xStart) * boxHeight); outputChannel = rChannel; outputChannel = ref Unsafe.AddByteOffset(ref outputChannel, 1); outputChannel = gChannel; outputChannel = ref Unsafe.AddByteOffset(ref outputChannel, 1); outputChannel = bChannel; outputChannel = ref Unsafe.AddByteOffset(ref outputChannel, 1); xStart = xEnd; x0 = x1; } outputTaskQueue = TaskHelper.SynchronizedTaskFactory.StartNew(async _ => { await encoder.WriteDataAsync(outputBuffer, cancellationToken: cancellationToken); }, null, cancellationToken); yStart = yEnd; } for (var y = 1; y < targetHeight; y++) { yEnd = yLookup[y]; (localRow0, localOffsetY0) = int.DivRem(yStart, TileHeight); MapRow(localRow0, localOffsetY0, xLookup, targetWidth, yStartMap); (localRow1, localOffsetY1) = int.DivRem(yEnd, TileHeight); MapRow(localRow1, localOffsetY1, xLookup, targetWidth, yEndMap); // Cross row if (localRow0 != localRow1) MapRow(localRow0, BottomPixelIndex, xLookup, targetWidth, yEndMap, true); xStart = OffsetX; x0 = 0; var outputBuffer = MemoryAllocator.Allocate(outputBufferSize); ref var outputChannel = ref outputBuffer.Span[0]; var boxHeight = yEnd - yStart; var xEnd = xLookup[0]; px = yEndMap[0]; px += yStartMap[^1]; px -= yEndMap[^1]; px -= yStartMap[0]; px /= Math.Max(1, (xEnd - xStart) * boxHeight); outputChannel = rChannel; outputChannel = ref Unsafe.AddByteOffset(ref outputChannel, 1); outputChannel = gChannel; outputChannel = ref Unsafe.AddByteOffset(ref outputChannel, 1); outputChannel = bChannel; outputChannel = ref Unsafe.AddByteOffset(ref outputChannel, 1); xStart = xEnd; for (int x1 = 1; x1 < targetWidth; x1++) { xEnd = xLookup[x1]; px = yEndMap[x1]; px += yStartMap[x0]; px -= yEndMap[x0]; px -= yStartMap[x1]; px /= Math.Max(1, (xEnd - xStart) * boxHeight); outputChannel = rChannel; outputChannel = ref Unsafe.AddByteOffset(ref outputChannel, 1); outputChannel = gChannel; outputChannel = ref Unsafe.AddByteOffset(ref outputChannel, 1); outputChannel = bChannel; outputChannel = ref Unsafe.AddByteOffset(ref outputChannel, 1); xStart = xEnd; x0 = x1; } outputTaskQueue = outputTaskQueue .ContinueWith(async _ => { await encoder.WriteDataAsync(outputBuffer, cancellationToken: cancellationToken); }, cancellationToken); yStart = yEnd; } await outputTaskQueue; await encoder.WriteEndOfFileAsync(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; var negative = sourceMap[0] - 1; var negativePixel = appendMode ? destinationSpan[^1] : Int32Pixel.Zero; if (negative >= 0) { negativePixel = readBufferSpan[negative]; } else if(currentTile.Column > 1) { // Cross row tile, no need to handle if it's first column tile for now // the provided asset is bordered black anyway TileManager.GetAdjacent(currentTile, -1, 0) .Integral .Acquire(yOffset, readBufferSpan); negativePixel = readBufferSpan[RightmostPixelIndex]; xAdder = readBufferSpan[RightmostPixelIndex]; } destinationSpan[^1] = negativePixel; 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); } }