public async Task <byte[]> ParseRectangle(PipeReader reader, RfbRectangleHeader header, PixelFormat format, CancellationToken token) { Debug.Assert(format.BitsPerPixel == 32); var ZRLEPipe = new Pipe(); var parser = Task.Run(() => ParsePixelData(ZRLEPipe.Reader, header, token)); // read compressed length var result = await reader.ReadMinBytesAsync(4, token); var length = BinaryPrimitives.ReadUInt32BigEndian(result.Buffer.ForceGetReadOnlySpan(4)); reader.AdvanceTo(result.Buffer.GetPosition(4)); long remaining = length; if (Fresh) { result = await reader.ReadMinBytesAsync(2, token); reader.AdvanceTo(result.Buffer.GetPosition(2)); remaining -= 2; Fresh = false; } // handle compressed rect while (remaining > 0) { result = await reader.ReadAsync(); long decompressed = 0; foreach (var segment in result.Buffer) { if (remaining > segment.Length) { await Decompress(ZRLEPipe.Writer, segment); decompressed += segment.Length; remaining -= segment.Length; } else { await Decompress(ZRLEPipe.Writer, segment.Slice(0, (int)remaining)); decompressed += remaining; remaining -= remaining; break; } } reader.AdvanceTo(result.Buffer.GetPosition(decompressed)); if (remaining > 0 && result.IsCompleted) { throw new VncConnectionException(); } } ZRLEPipe.Writer.Complete(); return(await parser); }
public async Task <byte[]> ParseRectangle(PipeReader reader, RfbRectangleHeader header, PixelFormat format, CancellationToken token) { Debug.Assert(format.BitsPerPixel == 32); var remainingPixels = header.Width * header.Height; var buf = ArrayPool <byte> .Shared.Rent(remainingPixels * 4); var destPos = 0; while (remainingPixels > 0) { var result = await reader.ReadAsync(token); byte[]? remainder = null; int read = 0; foreach (var segment in result.Buffer) { if (remainingPixels == 0) { break; } int s = 0; if (remainder != null) { if (remainder.Length + segment.Length < 4) // segment + remainder not big enough { var newRemainder = new byte[remainder.Length + segment.Length]; remainder.CopyTo(newRemainder, 0); segment.CopyTo(newRemainder.AsMemory(remainder.Length)); continue; } else // segment + remainder big enough { var completedReminder = new byte[4]; remainder.CopyTo(completedReminder, 0); segment.Span.Slice(0, 3 - remainder.Length).CopyTo(completedReminder.AsSpan(remainder.Length)); completedReminder.CopyTo(buf, destPos); buf[destPos + 3] = 0xff; remainingPixels--; read += 4; destPos += 4; s = 4 - remainder.Length; remainder = null; } } while (remainingPixels > 0) { if (s + 4 <= segment.Length) // segment big enough { segment.Slice(s, 3).CopyTo(buf.AsMemory(destPos)); buf[destPos + 3] = 0xff; remainingPixels--; read += 4; destPos += 4; s += 4; } else if (s + 4 > segment.Length) // segment not big enough { remainder = segment.Slice(s).ToArray(); break; } } } reader.AdvanceTo(result.Buffer.GetPosition(read)); if (remainingPixels > 0 && result.IsCompleted) { throw new VncConnectionException(); } } return(buf); }
private async Task <byte[]> ParsePixelData(PipeReader uncompressedReader, RfbRectangleHeader header, CancellationToken token) { byte[] data = ArrayPool <byte> .Shared.Rent(header.Width *header.Height * 4); Array.Clear(data, 0, data.Length); for (var y = 0; y < header.Height; y += 64) { var tileHeight = Math.Min(header.Height - y, 64); for (var x = 0; x < header.Width; x += 64) { var tileWidth = Math.Min(header.Width - x, 64); var result = await uncompressedReader.ReadMinBytesAsync(1, token); byte subencoding = result.Buffer.First.Span[0]; uncompressedReader.AdvanceTo(result.Buffer.GetPosition(1)); //Debug.WriteLine($"subencoding {subencoding}"); if (subencoding == 0) // Raw { await ParseRawTile(uncompressedReader, data, x, y, tileWidth, tileHeight, header.Width); } else if (subencoding == 1) // Solid { var pixel = await ParseCompressedPixel(uncompressedReader, token); for (int ty = 0; ty < tileHeight; ty++) { for (int tx = 0; tx < tileWidth; tx++) { Buffer.BlockCopy(pixel, 0, data, ( ((y + ty) * header.Width) + // y + ty rows (x + tx) // x + tx colums ) * 4, 4); // 4 bytes per pixel } } ArrayPool <byte> .Shared.Return(pixel); } else if (subencoding <= 16) // Packed Palette { await ParsePackedPaletteTile(uncompressedReader, data, x, y, tileWidth, tileHeight, header.Width, subencoding); } else if (17 <= subencoding && subencoding <= 127) { throw new InvalidDataException("Subencodings 17 to 127 are unused"); } else if (subencoding == 128) // Plain RLE { await ParsePlainRLETile(uncompressedReader, data, x, y, tileWidth, tileHeight, header.Width); } else if (130 <= subencoding && subencoding <= 255) // Palette RLE { await ParsePaletteRLETile(uncompressedReader, data, x, y, tileWidth, tileHeight, header.Width, subencoding - 128); } else { throw new NotImplementedException(); } } } return(data); }