public bool RespondToUpdateRequest(VncServerSession session) { var fb = Framebuffer; var fbr = session.FramebufferUpdateRequest; if (fb == null || fbr == null) { return(false); } var incremental = fbr.Incremental; var region = fbr.Region; session.FramebufferManualBeginUpdate(); var buffer = fb.GetBuffer(); int bpp = fb.PixelFormat.BytesPerPixel; lock (fb.SyncRoot) { int ymax = Math.Min(region.Y + region.Height, fb.Height); int xmax = Math.Min(region.X + region.Width, fb.Width); for (int y = region.Y; y < ymax; y += TileSize) { for (int x = region.X; x < xmax; x += TileSize) { int w = Math.Min(TileSize, xmax - x); int h = Math.Min(TileSize, ymax - y); var subregion = new VncRectangle(x, y, w, h); VncPixelFormat.Copy(buffer, fb.Stride, fb.PixelFormat, subregion, _pixelBuffer, w * bpp, Framebuffer.PixelFormat); int ix = x / TileSize, iy = y / TileSize; var tileHash = _hash.ComputeHash(_pixelBuffer, 0, w * h * bpp); if (_hashes[iy, ix] == null || !_hashes[iy, ix].SequenceEqual(tileHash)) { _hashes[iy, ix] = tileHash; if (incremental) { session.FramebufferManualInvalidate(subregion); } } } } } if (!incremental) { session.FramebufferManualInvalidate(region); } return(session.FramebufferManualEndUpdate()); }
/// <summary> /// Responds to a <see cref="VncServerSession"/> update request. /// </summary> /// <param name="session"> /// The session on which the update request was received. /// </param> /// <returns> /// <see langword="true"/> if the operation completed successfully; otherwise, /// <see langword="false"/>. /// </returns> public unsafe bool RespondToUpdateRequest(VncServerSession session) { VncRectangle subregion = default(VncRectangle); var fb = this.Framebuffer; var fbr = session.FramebufferUpdateRequest; if (fb == null || fbr == null) { return false; } var incremental = fbr.Incremental; var region = fbr.Region; int bpp = fb.PixelFormat.BytesPerPixel; session.FramebufferManualBeginUpdate(); // Take a lock here, as we will modify // both buffers heavily in the next block. lock (fb.SyncRoot) { lock (this.cachedFramebuffer.SyncRoot) { // Get the buffers (byte arrays) of data. We'll compare the data one pixel at a time. var actualBuffer = this.Framebuffer.GetBuffer(); var bufferedBuffer = this.cachedFramebuffer.GetBuffer(); for (int y = region.Y; y < region.Y + region.Height; y++) { subregion.X = region.X; subregion.Y = y; subregion.Width = region.Width; subregion.Height = 1; bool isValid = true; // For a given y, the x pixels are stored sequentially in the array // starting at y * stride (number of bytes per row); for each x // value there are bpp bytes of data (4 for a 32-bit integer); we are looking // for pixels between x and x + w so this translates to // y * stride + bpp * x and y * stride + bpp * (x + w) int srcOffset = (y * this.Framebuffer.Stride) + (bpp * region.X); int length = bpp * region.Width; fixed (byte* actualLinePtr = actualBuffer, bufferedLinePtr = bufferedBuffer) { isValid = NativeMethods.memcmp(actualLinePtr + srcOffset, bufferedLinePtr + srcOffset, (uint)length) == 0; } if (!isValid) { Buffer.BlockCopy(actualBuffer, srcOffset, bufferedBuffer, srcOffset, length); } this.isLineInvalid[y - region.Y] = !isValid; } } // lock } // lock if (incremental) { int? y = null; for (int line = 0; line < region.Height; line++) { if (y == null && this.isLineInvalid[line]) { y = region.Y + line; } if (y != null && (!this.isLineInvalid[line] || line == region.Height)) { // Flush subregion.X = region.X; subregion.Y = region.Y + y.Value; subregion.Width = region.Width; subregion.Height = line - y.Value; session.FramebufferManualInvalidate(subregion); y = null; } } } else { session.FramebufferManualInvalidate(region); } return session.FramebufferManualEndUpdate(); }
/// <summary> /// Responds to a <see cref="VncServerSession"/> update request. /// </summary> /// <param name="session"> /// The session on which the update request was received. /// </param> /// <returns> /// <see langword="true"/> if the operation completed successfully; otherwise, /// <see langword="false"/>. /// </returns> public unsafe bool RespondToUpdateRequest(VncServerSession session) { VncRectangle subregion = default(VncRectangle); var fb = this.Framebuffer; var fbr = session.FramebufferUpdateRequest; if (fb == null || fbr == null) { return(false); } var incremental = fbr.Incremental; var region = fbr.Region; int bpp = fb.PixelFormat.BytesPerPixel; session.FramebufferManualBeginUpdate(); // Take a lock here, as we will modify // both buffers heavily in the next block. lock (fb.SyncRoot) { lock (this.cachedFramebuffer.SyncRoot) { // Get the buffers (byte arrays) of data. We'll compare the data one pixel at a time. var actualBuffer = this.Framebuffer.GetBuffer(); var bufferedBuffer = this.cachedFramebuffer.GetBuffer(); for (int y = region.Y; y < region.Y + region.Height; y++) { subregion.X = region.X; subregion.Y = y; subregion.Width = region.Width; subregion.Height = 1; bool isValid = true; // For a given y, the x pixels are stored sequentially in the array // starting at y * stride (number of bytes per row); for each x // value there are bpp bytes of data (4 for a 32-bit integer); we are looking // for pixels between x and x + w so this translates to // y * stride + bpp * x and y * stride + bpp * (x + w) int srcOffset = (y * this.Framebuffer.Stride) + (bpp * region.X); int length = bpp * region.Width; fixed(byte *actualLinePtr = actualBuffer, bufferedLinePtr = bufferedBuffer) { isValid = NativeMethods.memcmp(actualLinePtr + srcOffset, bufferedLinePtr + srcOffset, (uint)length) == 0; } if (!isValid) { Buffer.BlockCopy(actualBuffer, srcOffset, bufferedBuffer, srcOffset, length); } this.isLineInvalid[y - region.Y] = !isValid; } } // lock } // lock if (incremental) { int?y = null; for (int line = 0; line < region.Height; line++) { if (y == null && this.isLineInvalid[line]) { y = region.Y + line; } if (y != null && (!this.isLineInvalid[line] || line == region.Height)) { // Flush subregion.X = region.X; subregion.Y = region.Y + y.Value; subregion.Width = region.Width; subregion.Height = line - y.Value; session.FramebufferManualInvalidate(subregion); y = null; } } } else { session.FramebufferManualInvalidate(region); } return(session.FramebufferManualEndUpdate()); }
/// <summary> /// Responds to a <see cref="VncServerSession"/> update request. /// </summary> /// <param name="session"> /// The session on which the update request was received. /// </param> /// <returns> /// <see langword="true"/> if the operation completed successfully; otherwise, /// <see langword="false"/>. /// </returns> public unsafe bool RespondToUpdateRequest(VncServerSession session) { VncRectangle subregion = default(VncRectangle); var fb = this.Framebuffer; var fbr = session.FramebufferUpdateRequest; if (fb == null || fbr == null) { return(false); } var incremental = fbr.Incremental; var region = fbr.Region; int bpp = fb.PixelFormat.BytesPerPixel; Logger.Debug($"Responding to an update request for region {region}."); session.FramebufferManualBeginUpdate(); // Take a lock here, as we will modify // both buffers heavily in the next block. lock (fb.SyncRoot) { lock (this.cachedFramebuffer.SyncRoot) { var actualBuffer = this.Framebuffer.GetBuffer(); var bufferedBuffer = this.cachedFramebuffer.GetBuffer(); // In this block, we will determine which rectangles need updating. Right now, we consider // each line at once. It's not a very efficient algorithm, but it works. // We're going to start at the upper-left position of the region, and then we will work our way down, // on a line by line basis, to determine if each line is still valid. // isLineInvalid will indicate, on a line-by-line basis, whether a line is still valid or not. for (int y = region.Y; y < region.Y + region.Height; y++) { subregion.X = region.X; subregion.Y = y; subregion.Width = region.Width; subregion.Height = 1; bool isValid = true; // For a given y, the x pixels are stored sequentially in the array // starting at y * stride (number of bytes per row); for each x // value there are bpp bytes of data (4 for a 32-bit integer); we are looking // for pixels between x and x + w so this translates to // y * stride + bpp * x and y * stride + bpp * (x + w) int srcOffset = (y * this.Framebuffer.Stride) + (bpp * region.X); int length = bpp * region.Width; fixed(byte *actualLinePtr = actualBuffer, bufferedLinePtr = bufferedBuffer) { isValid = NativeMethods.memcmp(actualLinePtr + srcOffset, bufferedLinePtr + srcOffset, (uint)length) == 0; } if (!isValid) { try { Buffer.BlockCopy(actualBuffer, srcOffset, bufferedBuffer, srcOffset, length); } catch { throw; } } this.isLineInvalid[y - region.Y] = !isValid; } } // lock } // lock if (incremental) { // Determine logical group of lines which are invalid. We find the first line which is invalid, // create a new region which contains the all invalid lines which immediately follow the current line. // If we find a valid line, we'll create a new region. int?y = null; for (int line = 0; line < region.Height; line++) { if (y == null && this.isLineInvalid[line]) { y = region.Y + line; } if (y != null && (!this.isLineInvalid[line] || line == region.Height - 1)) { // Flush subregion.X = region.X; subregion.Y = region.Y + y.Value; subregion.Width = region.Width; subregion.Height = line - y.Value + 1; session.FramebufferManualInvalidate(subregion); y = null; } } } else { session.FramebufferManualInvalidate(region); } return(session.FramebufferManualEndUpdate()); }