我已经很晚了。但是在动态生成视频时我遇到了完全相同的问题。通过重用缓冲区,我可以只为每一帧设置 R、G、B 值并且只设置 A 一次。
见下面的代码:
byte[] _workingBuffer = null;
byte[] GetProcessedPixelData(SKBitmap bitmap)
{
    ReadOnlySpan<byte> sourceSpan = bitmap.GetPixelSpan();
    if (_workingBuffer == null || _workingBuffer.Length != bitmap.ByteCount)
    {
        // Alloc buffer
        _workingBuffer = new byte[sourceSpan.Length];
        // Set all the alpha
        for (int i = 0; i < sourceSpan.Length; i += 4) _workingBuffer[i] = byte.MaxValue;
    }
    Stopwatch w = Stopwatch.StartNew();
    for (int i = 0; i < sourceSpan.Length; i += 4)
    {
        // A
        // Dont set alpha here. The alpha is already set in the buffer
        //_workingBuffer[i] = byte.MaxValue;
        //_workingBuffer[i] = sourceSpan[i + 3];
        // R
        _workingBuffer[i + 1] = sourceSpan[i];
        // G
        _workingBuffer[i + 2] = sourceSpan[i + 1];
        // B
        _workingBuffer[i + 3] = sourceSpan[i + 2];
    }
    Debug.Print("Copied " + sourceSpan.Length + " in " + w.Elapsed.TotalMilliseconds);
    return _workingBuffer;
}
这让我在 iPhone 上的 (1920 * 1080 * 4) 缓冲区大约需要 15 毫秒,约为 8mb。
这对我来说还远远不够。我的最终解决方案是执行偏移内存复制(C# 中的 Buffer.BlockCopy),因为 alpha 并不重要。
    byte[] _workingBuffer = null;
    byte[] GetProcessedPixelData(SKBitmap bitmap)
    {
        ReadOnlySpan<byte> sourceSpan = bitmap.GetPixelSpan();
        byte[] sourceArray = sourceSpan.ToArray();
        if (_workingBuffer == null || _workingBuffer.Length != bitmap.ByteCount)
        {
            // Alloc buffer
            _workingBuffer = new byte[sourceSpan.Length];
            // Set first byte. This is the alpha component of the first pixel
            _workingBuffer[0] = byte.MaxValue;
        }
        // Converts RGBA to ARGB in ~2 ms instead of ~15 ms
        // 
        // Copies the whole buffer with a offset of 1
        //                                      R   G   B   A   R   G   B   A   R   G   B   A
        // Originally the source buffer has:    R1, G1, B1, A1, R2, G2, B2, A2, R3, G3, B3, A3
        //                                   A  R   G   B   A   R   G   B   A   R   G   B   A
        // After the copy it looks like:     0, R1, G1, B1, A1, R2, G2, B2, A2, R3, G3, B3, A3
        // So essentially we get the wrong alpha for every pixel. But all alphas should be 255 anyways.
        // The first byte is set in the alloc
        Buffer.BlockCopy(sourceArray, 0, _workingBuffer, 1, sourceSpan.Length - 1);
        // Below is an inefficient method of converting RGBA to ARGB. Takes ~15 ms on iPhone 12 Pro Max for a 8mb buffer (1920 * 1080 * 4 bytes)
        /*
        for (int i = 0; i < sourceSpan.Length; i += 4)
        {
            // A
            // Dont set alpha here. The alpha is already set in the buffer
            //_workingBuffer[i] = byte.MaxValue;
            //_workingBuffer[i] = sourceSpan[i + 3];
            byte sR = sourceSpan[i];
            byte sG = sourceSpan[i + 1];
            byte sB = sourceSpan[i + 2];
            if (sR == 0 && sG == byte.MaxValue && sB == 0)
                continue;
            // R
            _workingBuffer[i + 1] = sR;
            // G
            _workingBuffer[i + 2] = sG;
            // B
            _workingBuffer[i + 3] = sB;
        }
        */
        return _workingBuffer;
    }
代码注释了它是如何工作的。在我的同一部 iPhone 上,大约需要 2 毫秒,这对于我的用例来说已经足够了。