// Copy tile to output
        private void CopyToOutput(TileRenderState state, Tensor <float> tensor, Rectangle tile)
        {
            if (tile.Y > 0)
            {
                BlendTopMargin(state, tensor, tile);
            }

            var sx = tile.X > 0 ? Margin : 0;
            var sy = tile.Y > 0 ? Margin : 0;

            var(x, y) = (tile.X, tile.Y);
            var cols = Math.Min(tensor.Width() - sx, Output.Width - tile.X);
            var rows = Math.Min(tensor.Height() - sy, Output.Height - tile.Y);

            var tensorRow  = new ArraySegment <byte>(state.TensorRow, 0, cols * 4);
            var marginLeft = new ArraySegment <byte>(state.OutputRow, 0, sx * 4);

            for (int row = sy; row < rows; ++row)
            {
                _ = tensor.GetRowArgb(tensorRow, sx, row + sy);
                Output.GetRowArgb(marginLeft, tile.X, row + y);
                marginLeft.LinearBlend(tensorRow, tensorRow);
                Output.SetRowArgb(tensorRow, x, row + y);
            }
        }
        // Blend a tile's top rows with previous output
        private void BlendTopMargin(TileRenderState state, Tensor <float> tensor, Rectangle tile)
        {
            var(ix, iy) = (tile.X > 0 ? Margin : 0, Margin);
            var(ox, oy) = (tile.X, tile.Y);
            var delta     = 1.0f / Margin;
            var c         = delta;
            var elements  = (tensor.Width() - ix) * 4;
            var tensorRow = new ArraySegment <byte>(state.TensorRow, 0, elements);
            var outputRow = new ArraySegment <byte>(state.OutputRow, 0, elements);
            var resultRow = new ArraySegment <byte>(state.BufferRow, 0, elements);

            for (int i = 0; i < Margin; ++i, ++iy, ++oy, c += delta)
            {
                _ = tensor.GetRowArgb(tensorRow, ix, iy);
                _ = Output.GetRowArgb(outputRow, ox, oy);
                outputRow.MixArgb(tensorRow, resultRow, c);
                Output.SetRowArgb(resultRow, ox, oy);
            }
        }