Ejemplo n.º 1
0
        /// <inheritdoc/>s
        public bool FramebufferManualEndUpdate()
        {
            var fb = this.Framebuffer;

            if (this.clientWidth != fb.Width || this.clientHeight != fb.Height)
            {
                if (this.clientEncoding.Contains(VncEncoding.PseudoDesktopSize))
                {
                    var region = new VncRectangle(0, 0, fb.Width, fb.Height);
                    this.AddRegion(region, VncEncoding.PseudoDesktopSize, new byte[0]);
                    this.clientWidth  = this.Framebuffer.Width;
                    this.clientHeight = this.Framebuffer.Height;
                }
            }

            if (this.fbuRectangles.Count == 0)
            {
                return(false);
            }

            this.FramebufferUpdateRequest = null;

            this.SendRectangles(this.fbuRectangles);
            this.fbuRectangles.Clear();
            return(true);
        }
Ejemplo n.º 2
0
        private void HandleFramebufferUpdateRequest()
        {
            var incremental = this.c.ReceiveByte() != 0;
            var region      = this.c.ReceiveRectangle();

            if (!incremental && this.ClientEncodings.Contains(VncEncoding.ExtendedDesktopSize) && this.fbSource.SupportsResizing)
            {
                List <Rectangle> rectangles = new List <Rectangle>();
                rectangles.Add(
                    this.GetExtendedDesktopSizeRectangle(
                        ExtendedDesktopSizeReason.External,
                        ExtendedDesktopSizeStatus.Success));
                this.SendRectangles(rectangles);
            }

            lock (this.FramebufferUpdateRequestLock)
            {
                this.logger?.LogDebug($"Received a FramebufferUpdateRequest command for {region}");

                region = VncRectangle.Intersect(region, new VncRectangle(0, 0, this.Framebuffer.Width, this.Framebuffer.Height));

                if (region.IsEmpty)
                {
                    return;
                }

                this.FramebufferUpdateRequest = new FramebufferUpdateRequest(incremental, region);
                this.FramebufferChanged();
            }
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Copies a region of the framebuffer into a bitmap.
        /// </summary>
        /// <param name="source">The framebuffer to read.</param>
        /// <param name="sourceRectangle">The framebuffer region to copy.</param>
        /// <param name="target">The bitmap to copy into.</param>
        /// <param name="targetX">The leftmost X coordinate of the bitmap to draw to.</param>
        /// <param name="targetY">The topmost Y coordinate of the bitmap to draw to.</param>
        public unsafe static void CopyFromFramebuffer(VncFramebuffer source, VncRectangle sourceRectangle,
                                                      Bitmap target, int targetX, int targetY)
        {
            Throw.If.Null(source, "source").Null(target, "target");
            if (sourceRectangle.IsEmpty)
            {
                return;
            }

            var winformsRect = new Rectangle(targetX, targetY, sourceRectangle.Width, sourceRectangle.Height);
            var data         = target.LockBits(winformsRect, ImageLockMode.WriteOnly, PixelFormat.Format32bppRgb);

            try
            {
                fixed(byte *framebufferData = source.GetBuffer())
                {
                    VncPixelFormat.Copy((IntPtr)framebufferData, source.Stride, source.PixelFormat, sourceRectangle,
                                        data.Scan0, data.Stride, new VncPixelFormat());
                }
            }
            finally
            {
                target.UnlockBits(data);
            }
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Queues an update corresponding to one region of the framebuffer being copied to another.
        /// </summary>
        /// <param name="target">
        /// The updated <see cref="VncRectangle"/>.
        /// </param>
        /// <param name="sourceX">
        /// The X coordinate of the source.
        /// </param>
        /// <param name="sourceY">
        /// The Y coordinate of the source.
        /// </param>
        /// <remarks>
        /// Do not call this method without holding <see cref="VncServerSession.FramebufferUpdateRequestLock"/>.
        /// </remarks>
        public void FramebufferManualCopyRegion(VncRectangle target, int sourceX, int sourceY)
        {
            if (!this.clientEncoding.Contains(VncEncoding.CopyRect))
            {
                var source = new VncRectangle(sourceX, sourceY, target.Width, target.Height);
                var region = VncRectangle.Union(source, target);

                if (region.Area > source.Area + target.Area)
                {
                    this.FramebufferManualInvalidate(new[] { source, target });
                }
                else
                {
                    this.FramebufferManualInvalidate(region);
                }

                return;
            }

            var contents = new byte[4];

            VncUtility.EncodeUInt16BE(contents, 0, (ushort)sourceX);
            VncUtility.EncodeUInt16BE(contents, 2, (ushort)sourceY);
            this.AddRegion(target, VncEncoding.CopyRect, contents);
        }
Ejemplo n.º 5
0
        /// <summary>
        /// 将需要更新位图的区域复制到framebuffer中
        /// </summary>
        /// <param name="source">图片源.</param>
        /// <param name="sourceRectangle">要复制的位图区域.</param>
        /// <param name="target">目标帧缓冲区</param>
        /// <param name="targetX">帧缓冲区的左顶点X坐标</param>
        /// <param name="targetY">帧缓冲区的左顶点Y坐标</param>
        public unsafe static void CopyToFramebuffer(Bitmap source, VncRectangle sourceRectangle,
                                                    VncFramebuffer target, int targetX, int targetY)
        {
            Throw.If.Null(source, "source").Null(target, "target");
            if (sourceRectangle.IsEmpty)
            {
                return;
            }

            var winformsRect = new Rectangle(sourceRectangle.X, sourceRectangle.Y, sourceRectangle.Width, sourceRectangle.Height);
            var data         = source.LockBits(winformsRect, ImageLockMode.ReadOnly, PixelFormat.Format32bppRgb);

            try
            {
                fixed(byte *framebufferData = target.GetBuffer())
                {
                    VncPixelFormat.Copy(data.Scan0, data.Stride, new VncPixelFormat(32, 24, 8, 16, 8, 8, 8, 0), sourceRectangle,
                                        (IntPtr)framebufferData, target.Stride, target.PixelFormat, targetX, targetY);
                }
            }
            finally
            {
                source.UnlockBits(data);
            }
        }
Ejemplo n.º 6
0
        /// <inheritdoc/>
        public void FramebufferManualInvalidate(VncRectangle region)
        {
            var fb  = this.Framebuffer;
            var cpf = this.clientPixelFormat;

            region = VncRectangle.Intersect(region, new VncRectangle(0, 0, this.clientWidth, this.clientHeight));
            if (region.IsEmpty)
            {
                return;
            }

            int x = region.X, y = region.Y, w = region.Width, h = region.Height, bpp = cpf.BytesPerPixel;
            var contents = new byte[w * h * bpp];

            VncPixelFormat.Copy(
                fb.GetBuffer(),
                fb.Width,
                fb.Stride,
                fb.PixelFormat,
                region,
                contents,
                w,
                w * bpp,
                cpf);

            this.AddRegion(region, VncEncoding.Raw, contents);
        }
        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());
        }
Ejemplo n.º 8
0
        /// <inheritdoc/>
        public override void Send(Stream stream, VncPixelFormat pixelFormat, VncRectangle region, byte[] contents)
        {
            this.buffer.SetLength(0);
            this.deflater.Write(contents, 0, contents.Length);
            this.deflater.Flush();
            this.buffer.Position = 0;

            byte[] length = new byte[4];
            VncUtility.EncodeUInt32BE(length, 0, (uint)this.buffer.Length);
            stream.Write(length, 0, 4);

            this.buffer.CopyTo(stream);
        }
Ejemplo n.º 9
0
        /// <inheritdoc/>
        public void FramebufferManualInvalidate(VncRectangle region)
        {
            var fb  = this.Framebuffer;
            var cpf = this.clientPixelFormat;

            region = VncRectangle.Intersect(region, new VncRectangle(0, 0, this.clientWidth, this.clientHeight));
            if (region.IsEmpty)
            {
                return;
            }

            int x = region.X, y = region.Y, w = region.Width, h = region.Height, bpp = cpf.BytesPerPixel;
            var contents = new byte[w * h * bpp];

            VncPixelFormat.Copy(
                fb.GetBuffer(),
                fb.Width,
                fb.Stride,
                fb.PixelFormat,
                region,
                contents,
                w,
                w * bpp,
                cpf);

#if DEFLATESTREAM_FLUSH_WORKS
            if (_clientEncoding.Contains(VncEncoding.Zlib))
            {
                _zlibMemoryStream.Position = 0;
                _zlibMemoryStream.SetLength(0);
                _zlibMemoryStream.Write(new byte[4], 0, 4);

                if (_zlibDeflater == null)
                {
                    _zlibMemoryStream.Write(new[] { (byte)120, (byte)218 }, 0, 2);
                    _zlibDeflater = new DeflateStream(_zlibMemoryStream, CompressionMode.Compress, false);
                }

                _zlibDeflater.Write(contents, 0, contents.Length);
                _zlibDeflater.Flush();
                contents = _zlibMemoryStream.ToArray();

                VncUtility.EncodeUInt32BE(contents, 0, (uint)(contents.Length - 4));
                AddRegion(region, VncEncoding.Zlib, contents);
            }
            else
#endif
            {
                this.AddRegion(region, VncEncoding.Raw, contents);
            }
        }
Ejemplo n.º 10
0
        void AddRegion(VncRectangle region, VncEncoding encoding, byte[] contents)
        {
            _fbuRectangles.Add(new Rectangle()
            {
                Region = region, Encoding = encoding, Contents = contents
            });

            // Avoid the overflow of updated rectangle count.
            // NOTE: EndUpdate may implicitly add one for desktop resizing.
            if (_fbuRectangles.Count >= ushort.MaxValue - 1)
            {
                FramebufferManualEndUpdate(); FramebufferManualBeginUpdate();
            }
        }
Ejemplo n.º 11
0
        /// <summary>
        /// Copies a region of a bitmap into the framebuffer.
        /// </summary>
        /// <param name="source">The bitmap to read.</param>
        /// <param name="sourceRectangle">The bitmap region to copy.</param>
        /// <param name="target">The framebuffer to copy into.</param>
        /// <param name="targetX">The leftmost X coordinate of the framebuffer to draw to.</param>
        /// <param name="targetY">The topmost Y coordinate of the framebuffer to draw to.</param>
        public static unsafe void CopyToFramebuffer(
            Bitmap source,
            VncRectangle sourceRectangle,
            VncFramebuffer target,
            int targetX,
            int targetY)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            if (target == null)
            {
                throw new ArgumentNullException(nameof(target));
            }

            if (sourceRectangle.IsEmpty)
            {
                return;
            }

            var winformsRect = new Rectangle(sourceRectangle.X, sourceRectangle.Y, sourceRectangle.Width, sourceRectangle.Height);
            var data         = source.LockBits(winformsRect, ImageLockMode.ReadOnly, PixelFormat.Format32bppRgb);

            try
            {
                fixed(byte *framebufferData = target.GetBuffer())
                {
                    VncPixelFormat.Copy(
                        data.Scan0,
                        data.Stride,
                        new VncPixelFormat(),
                        sourceRectangle,
                        (IntPtr)framebufferData,
                        target.Stride,
                        target.PixelFormat,
                        targetX,
                        targetY);
                }
            }
            finally
            {
                source.UnlockBits(data);
            }
        }
Ejemplo n.º 12
0
        /// <inheritdoc/>s
        public bool FramebufferManualEndUpdate()
        {
            var fb = this.Framebuffer;

            if (this.clientWidth != fb.Width || this.clientHeight != fb.Height)
            {
                if (this.clientEncoding.Contains(VncEncoding.PseudoDesktopSize))
                {
                    var region = new VncRectangle(0, 0, fb.Width, fb.Height);
                    this.AddRegion(region, VncEncoding.PseudoDesktopSize, new byte[0]);
                    this.clientWidth  = this.Framebuffer.Width;
                    this.clientHeight = this.Framebuffer.Height;
                }
            }

            if (this.fbuRectangles.Count == 0)
            {
                return(false);
            }

            this.FramebufferUpdateRequest = null;

            lock (this.c.SyncRoot)
            {
                this.c.Send(new byte[2] {
                    0, 0
                });
                this.c.SendUInt16BE((ushort)this.fbuRectangles.Count);

                foreach (var rectangle in this.fbuRectangles)
                {
                    Debug.Assert(rectangle.Encoding == VncEncoding.Raw, "Rectangles should be in raw format");

                    this.c.SendRectangle(rectangle.Region);
                    this.c.SendUInt32BE((uint)this.Encoder.Encoding);

                    this.Encoder.Send(this.c.Stream, this.clientPixelFormat, rectangle.Region, rectangle.Contents);
                }

                this.fbuRectangles.Clear();
                return(true);
            }
        }
Ejemplo n.º 13
0
        private void HandleFramebufferUpdateRequest()
        {
            var incremental = this.c.ReceiveByte() != 0;
            var region      = this.c.ReceiveRectangle();

            lock (this.FramebufferUpdateRequestLock)
            {
                this.logger?.Log(LogLevel.Info, () => $"Received a FramebufferUpdateRequest command for {region}");

                region = VncRectangle.Intersect(region, new VncRectangle(0, 0, this.Framebuffer.Width, this.Framebuffer.Height));

                if (region.IsEmpty)
                {
                    return;
                }

                this.FramebufferUpdateRequest = new FramebufferUpdateRequest(incremental, region);
                this.FramebufferChanged();
            }
        }
Ejemplo n.º 14
0
        /// <summary>
        /// Copies a region of the framebuffer into a bitmap.
        /// </summary>
        /// <param name="source">The framebuffer to read.</param>
        /// <param name="sourceRectangle">The framebuffer region to copy.</param>
        /// <param name="target">The bitmap to copy into.</param>
        /// <param name="targetX">The leftmost X coordinate of the bitmap to draw to.</param>
        /// <param name="targetY">The topmost Y coordinate of the bitmap to draw to.</param>
        public unsafe static void CopyFromFramebuffer(VncFramebuffer source, VncRectangle sourceRectangle,
                                                      Bitmap target, int targetX, int targetY)
        {
            Throw.If.Null(source, "source").Null(target, "target");
            if (sourceRectangle.IsEmpty) { return; }

            var winformsRect = new Rectangle(targetX, targetY, sourceRectangle.Width, sourceRectangle.Height);
            var data = target.LockBits(winformsRect, ImageLockMode.WriteOnly, PixelFormat.Format32bppRgb);
            try
            {
                fixed (byte* framebufferData = source.GetBuffer())
                {
                    VncPixelFormat.Copy((IntPtr)framebufferData, source.Stride, source.PixelFormat, sourceRectangle,
                                        data.Scan0, data.Stride, new VncPixelFormat());
                }
            }
            finally
            {
                target.UnlockBits(data);
            }
        }
Ejemplo n.º 15
0
        /// <inheritdoc/>
        public override int Send(Stream stream, VncPixelFormat pixelFormat, VncRectangle region, byte[] contents)
        {
            var jpegQualityLevel = GetQualityLevel(this.VncServerSession);

            // The JPEG compression currently assumes a RGB32 pixel format, fall back to basic compression
            // when this pixel format is not available.
            // Additionally, a minimal JPEG image is at least ~128 bytes in size, so only compress to JPEG
            // when the uncompressed file significantly larger.
            if (this.Compression != TightCompression.Jpeg ||
                !VncPixelFormat.RGB32.Equals(pixelFormat) ||
                region.IsEmpty ||
                contents.Length < 256 ||
                jpegQualityLevel == 0)
            {
                return(this.SendWithBasicCompression(stream, pixelFormat, region, contents));
            }
            else
            {
                return(this.SendWithJpegCompression(stream, pixelFormat, region, contents, jpegQualityLevel));
            }
        }
Ejemplo n.º 16
0
        /// <inheritdoc/>
        public void FramebufferManualInvalidate(VncRectangle region)
        {
            var fb  = this.Framebuffer;
            var cpf = this.clientPixelFormat;

            region = VncRectangle.Intersect(region, new VncRectangle(0, 0, this.clientWidth, this.clientHeight));
            if (region.IsEmpty)
            {
                return;
            }

            int x = region.X, y = region.Y, w = region.Width, h = region.Height, bpp = cpf.BytesPerPixel;
            var contents = new byte[w * h * bpp];

            VncPixelFormat.Copy(
                fb.GetBuffer(),
                fb.Width,
                fb.Stride,
                fb.PixelFormat,
                region,
                contents,
                w,
                w * bpp,
                cpf);

            if (clientEncoding.Contains(VncEncoding.Zlib))
            {
                byte[] zlibContents = Compress(contents);
                var    lenArray     = new byte[4];
                VncUtility.EncodeUInt32BE(lenArray, 0, (uint)(zlibContents.Length));
                var finArray = Combine(lenArray, zlibContents);
                AddRegion(region, VncEncoding.Zlib, finArray);
            }
            else
            {
                this.AddRegion(region, VncEncoding.Raw, contents);
            }
        }
Ejemplo n.º 17
0
        /// <summary>
        /// Completes a manual framebuffer update.
        ///
        /// Do not call this method without holding <see cref="VncServerSession.FramebufferUpdateRequestLock"/>.
        /// </summary>
        public bool FramebufferManualEndUpdate()
        {
            var fb = Framebuffer;

            if (_clientWidth != fb.Width || _clientHeight != fb.Height)
            {
                if (_clientEncoding.Contains(VncEncoding.PseudoDesktopSize))
                {
                    var region = new VncRectangle(0, 0, fb.Width, fb.Height);
                    AddRegion(region, VncEncoding.PseudoDesktopSize, new byte[0]);
                    _clientWidth = Framebuffer.Width; _clientHeight = Framebuffer.Height;
                }
            }

            if (_fbuRectangles.Count == 0)
            {
                return(false);
            }
            FramebufferUpdateRequest = null;

            lock (_c.SyncRoot)
            {
                _c.Send(new byte[2] {
                    0, 0
                });
                _c.SendUInt16BE((ushort)_fbuRectangles.Count);

                foreach (var rectangle in _fbuRectangles)
                {
                    _c.SendRectangle(rectangle.Region);
                    _c.SendUInt32BE((uint)rectangle.Encoding);
                    _c.Send(rectangle.Contents);
                }

                _fbuRectangles.Clear(); return(true);
            }
        }
Ejemplo n.º 18
0
        /// <summary>
        /// Copies a region of the framebuffer into a bitmap.
        /// </summary>
        /// <param name="source">The framebuffer to read.</param>
        /// <param name="sourceRectangle">The framebuffer region to copy.</param>
        /// <param name="target">The bitmap to copy into.</param>
        /// <param name="targetX">The leftmost X coordinate of the bitmap to draw to.</param>
        /// <param name="targetY">The topmost Y coordinate of the bitmap to draw to.</param>
        public static unsafe void CopyFromFramebuffer(
            VncFramebuffer source,
            VncRectangle sourceRectangle,
            Bitmap target,
            int targetX,
            int targetY)
        {
            if (target == null)
            {
                throw new ArgumentNullException(nameof(target));
            }

            var winformsRect = new Rectangle(targetX, targetY, sourceRectangle.Width, sourceRectangle.Height);
            var data         = target.LockBits(winformsRect, ImageLockMode.WriteOnly, PixelFormat.Format32bppRgb);

            try
            {
                VncPixelFormat.CopyFromFramebuffer(source, sourceRectangle, data.Scan0, data.Stride, targetX, targetY);
            }
            finally
            {
                target.UnlockBits(data);
            }
        }
Ejemplo n.º 19
0
        private void AddRegion(VncRectangle region, VncEncoding encoding, byte[] contents)
        {
            this.fbuRectangles.Add(new Rectangle() { Region = region, Encoding = encoding, Contents = contents });

            // Avoid the overflow of updated rectangle count.
            // NOTE: EndUpdate may implicitly add one for desktop resizing.
            if (this.fbuRectangles.Count >= ushort.MaxValue - 1)
            {
                this.FramebufferManualEndUpdate();
                this.FramebufferManualBeginUpdate();
            }
        }
Ejemplo n.º 20
0
        /// <summary>
        /// Completes a manual framebuffer update.
        /// </summary>
        /// <returns>
        /// <see langword="true"/> if the operation completed successfully; otherwise,
        /// <see langword="false"/>.
        /// </returns>
        /// <remarks>
        /// Do not call this method without holding <see cref="VncServerSession.FramebufferUpdateRequestLock"/>.
        /// </remarks>
        public bool FramebufferManualEndUpdate()
        {
            var fb = this.Framebuffer;
            if (this.clientWidth != fb.Width || this.clientHeight != fb.Height)
            {
                if (this.clientEncoding.Contains(VncEncoding.PseudoDesktopSize))
                {
                    var region = new VncRectangle(0, 0, fb.Width, fb.Height);
                    this.AddRegion(region, VncEncoding.PseudoDesktopSize, new byte[0]);
                    this.clientWidth = this.Framebuffer.Width;
                    this.clientHeight = this.Framebuffer.Height;
                }
            }

            if (this.fbuRectangles.Count == 0)
            {
                return false;
            }

            this.FramebufferUpdateRequest = null;

            lock (this.c.SyncRoot)
            {
                this.c.Send(new byte[2] { 0, 0 });
                this.c.SendUInt16BE((ushort)this.fbuRectangles.Count);

                foreach (var rectangle in this.fbuRectangles)
                {
                    this.c.SendRectangle(rectangle.Region);
                    this.c.SendUInt32BE((uint)rectangle.Encoding);
                    this.c.Send(rectangle.Contents);
                }

                this.fbuRectangles.Clear();
                return true;
            }
        }
Ejemplo n.º 21
0
 /// <summary>
 /// Queues an update for each of the specified regions.
 ///
 /// Do not call this method without holding <see cref="VncServerSession.FramebufferUpdateRequestLock"/>.
 /// </summary>
 /// <param name="regions">The regions to invalidate.</param>
 public void FramebufferManualInvalidate(VncRectangle[] regions)
 {
     Throw.If.Null(regions, "regions");
     foreach (var region in regions)
     {
         this.FramebufferManualInvalidate(region);
     }
 }
Ejemplo n.º 22
0
        /// <summary>
        /// Queues an update for the specified region.
        ///
        /// Do not call this method without holding <see cref="VncServerSession.FramebufferUpdateRequestLock"/>.
        /// </summary>
        /// <param name="region">The region to invalidate.</param>
        public void FramebufferManualInvalidate(VncRectangle region)
        {
            var fb = this.Framebuffer;
            var cpf = this.clientPixelFormat;
            region = VncRectangle.Intersect(region, new VncRectangle(0, 0, this.clientWidth, this.clientHeight));
            if (region.IsEmpty)
            {
                return;
            }

            int x = region.X, y = region.Y, w = region.Width, h = region.Height, bpp = cpf.BytesPerPixel;
            var contents = new byte[w * h * bpp];

            VncPixelFormat.Copy(
                fb.GetBuffer(),
                fb.Width,
                fb.Stride,
                fb.PixelFormat,
                region,
                contents,
                w,
                w * bpp,
                cpf);

#if DEFLATESTREAM_FLUSH_WORKS
            if (_clientEncoding.Contains(VncEncoding.Zlib))
            {
                _zlibMemoryStream.Position = 0;
                _zlibMemoryStream.SetLength(0);
                _zlibMemoryStream.Write(new byte[4], 0, 4);

                if (_zlibDeflater == null)
                {
                    _zlibMemoryStream.Write(new[] { (byte)120, (byte)218 }, 0, 2);
                    _zlibDeflater = new DeflateStream(_zlibMemoryStream, CompressionMode.Compress, false);
                }

                _zlibDeflater.Write(contents, 0, contents.Length);
                _zlibDeflater.Flush();
                contents = _zlibMemoryStream.ToArray();

                VncUtility.EncodeUInt32BE(contents, 0, (uint)(contents.Length - 4));
                AddRegion(region, VncEncoding.Zlib, contents);
            }
            else
#endif
            {
                this.AddRegion(region, VncEncoding.Raw, contents);
            }
        }
Ejemplo n.º 23
0
        /// <summary>
        /// Queues an update corresponding to one region of the framebuffer being copied to another.
        /// </summary>
        /// <param name="target">
        /// The updated <see cref="VncRectangle"/>.
        /// </param>
        /// <param name="sourceX">
        /// The X coordinate of the source.
        /// </param>
        /// <param name="sourceY">
        /// The Y coordinate of the source.
        /// </param>
        /// <remarks>
        /// Do not call this method without holding <see cref="VncServerSession.FramebufferUpdateRequestLock"/>.
        /// </remarks>
        public void FramebufferManualCopyRegion(VncRectangle target, int sourceX, int sourceY)
        {
            if (!this.clientEncoding.Contains(VncEncoding.CopyRect))
            {
                var source = new VncRectangle(sourceX, sourceY, target.Width, target.Height);
                var region = VncRectangle.Union(source, target);

                if (region.Area > source.Area + target.Area)
                {
                    this.FramebufferManualInvalidate(new[] { source, target });
                }
                else
                {
                    this.FramebufferManualInvalidate(region);
                }

                return;
            }

            var contents = new byte[4];
            VncUtility.EncodeUInt16BE(contents, 0, (ushort)sourceX);
            VncUtility.EncodeUInt16BE(contents, 2, (ushort)sourceY);
            this.AddRegion(target, VncEncoding.CopyRect, contents);
        }
 /// <summary>
 /// Initializes a new instance of the <see cref="FramebufferUpdateRequest"/> class.
 /// </summary>
 /// <param name="incremental">Whether an incremental update is desired.</param>
 /// <param name="region">The region to update.</param>
 public FramebufferUpdateRequest(bool incremental, VncRectangle region)
 {
     this.Incremental = incremental;
     this.Region      = region;
 }
Ejemplo n.º 25
0
        /// <summary>
        /// <para>
        /// Copies pixels. A format conversion is performed if necessary.
        /// </para>
        /// <para>
        /// Be sure to lock <see cref="VncFramebuffer.SyncRoot"/> first to avoid tearing,
        /// if the connection is active.
        /// </para>
        /// </summary>
        /// <param name="source">A pointer to the upper-left corner of the source.</param>
        /// <param name="sourceStride">The offset in the source between one Y coordinate and the next.</param>
        /// <param name="sourceFormat">The source pixel format.</param>
        /// <param name="sourceRectangle">The rectangle in the source to decode.</param>
        /// <param name="target">A pointer to the upper-left corner of the target.</param>
        /// <param name="targetStride">The offset in the target between one Y coordinate and the next.</param>
        /// <param name="targetFormat">The target pixel format.</param>
        /// <param name="targetX">The X coordinate in the target that the leftmost pixel should be placed into.</param>
        /// <param name="targetY">The Y coordinate in the target that the topmost pixel should be placed into.</param>
        public static unsafe void Copy(
            IntPtr source,
            int sourceStride,
            VncPixelFormat sourceFormat,
            VncRectangle sourceRectangle,
            IntPtr target,
            int targetStride,
            VncPixelFormat targetFormat,
            int targetX = 0,
            int targetY = 0)
        {
            Throw.If.True(source == IntPtr.Zero, "source").True(target == IntPtr.Zero, "target");
            Throw.If.Null(sourceFormat, "sourceFormat").Null(targetFormat, "targetFormat");

            if (sourceRectangle.IsEmpty)
            {
                return;
            }

            int x = sourceRectangle.X, w = sourceRectangle.Width;
            int y = sourceRectangle.Y, h = sourceRectangle.Height;

            var sourceData = (byte*)(void*)source + (y * sourceStride) + (x * sourceFormat.BytesPerPixel);
            var targetData = (byte*)(void*)target + (targetY * targetStride) + (targetX * targetFormat.BytesPerPixel);

            if (sourceFormat.Equals(targetFormat))
            {
                for (int iy = 0; iy < h; iy++)
                {
                    if (sourceFormat.BytesPerPixel == 4)
                    {
                        uint* sourceDataX0 = (uint*)sourceData, targetDataX0 = (uint*)targetData;
                        for (int ix = 0; ix < w; ix++)
                        {
                            *targetDataX0++ = *sourceDataX0++;
                        }
                    }
                    else
                    {
                        int bytes = w * sourceFormat.BytesPerPixel;
                        byte* sourceDataX0 = (byte*)sourceData, targetDataX0 = (byte*)targetData;
                        for (int ib = 0; ib < bytes; ib++)
                        {
                            *targetDataX0++ = *sourceDataX0++;
                        }
                    }

                    sourceData += sourceStride;
                    targetData += targetStride;
                }
            }
        }
Ejemplo n.º 26
0
        /// <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(IVncServerSession 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;

            this.logger?.Log(LogLevel.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;

                        // 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;

                        var isValid = actualBuffer.AsSpan().Slice(srcOffset, length)
                                      .SequenceCompareTo(bufferedBuffer.AsSpan().Slice(srcOffset, 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());
        }
Ejemplo n.º 27
0
 /// <summary>
 /// Writes a <see cref="VncRectangle"/> to the current position in the stream and advances the position within the stream by 8 bytes.
 /// </summary>
 /// <param name="region">
 /// The <see cref="VncRectangle"/> to write to the stream.
 /// </param>
 public void SendRectangle(VncRectangle region)
 {
     var buffer = new byte[8];
     VncUtility.EncodeUInt16BE(buffer, 0, (ushort)region.X);
     VncUtility.EncodeUInt16BE(buffer, 2, (ushort)region.Y);
     VncUtility.EncodeUInt16BE(buffer, 4, (ushort)region.Width);
     VncUtility.EncodeUInt16BE(buffer, 6, (ushort)region.Height);
     this.Send(buffer);
 }
Ejemplo n.º 28
0
        /// <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());
        }
Ejemplo n.º 29
0
 /// <inheritdoc/>
 public override void Send(Stream stream, VncPixelFormat pixelFormat, VncRectangle region, byte[] contents)
 {
     stream.Write(contents, 0, contents.Length);
 }
Ejemplo n.º 30
0
        /// <summary>
        /// Copies pixels between two byte arrays. A format conversion is performed if necessary.
        ///
        /// Be sure to lock <see cref="VncFramebuffer.SyncRoot"/> first to avoid tearing,
        /// if the connection is active.
        /// </summary>
        /// <param name="source">A pointer to the upper-left corner of the source.</param>
        /// <param name="sourceWidth">The width of the source image.</param>
        /// <param name="sourceStride">The offset in the source between one Y coordinate and the next.</param>
        /// <param name="sourceFormat">The source pixel format.</param>
        /// <param name="sourceRectangle">The rectangle in the source to decode.</param>
        /// <param name="target">A pointer to the upper-left corner of the target.</param>
        /// <param name="targetWidth">The width of the target image.</param>
        /// <param name="targetStride">The offset in the target between one Y coordinate and the next.</param>
        /// <param name="targetFormat">The target pixel format.</param>
        /// <param name="targetX">The X coordinate in the target that the leftmost pixel should be placed into.</param>
        /// <param name="targetY">The Y coordinate in the target that the topmost pixel should be placed into.</param>
        public static unsafe void Copy(
            byte[] source,
            int sourceWidth,
            int sourceStride,
            VncPixelFormat sourceFormat,
            VncRectangle sourceRectangle,
            byte[] target,
            int targetWidth,
            int targetStride,
            VncPixelFormat targetFormat,
            int targetX = 0,
            int targetY = 0)
        {
            Throw.If.Null(source, "source").Null(target, "target");

            if (sourceRectangle.IsEmpty)
            {
                return;
            }

            int x = sourceRectangle.X, w = sourceRectangle.Width;
            int y = sourceRectangle.Y, h = sourceRectangle.Height;

            if (sourceFormat.Equals(targetFormat))
            {
                if (sourceRectangle.Width == sourceWidth
                    && sourceWidth == targetWidth
                    && sourceStride == targetStride)
                {
                    int sourceStart = sourceStride * y;
                    int length = targetStride * h;

                    Buffer.BlockCopy(source, sourceStart, target, 0, length);
                }
                else
                {
                    for (int iy = 0; iy < h; iy++)
                    {
                        int sourceStart = (sourceStride * (iy + y)) + (x * sourceFormat.BitsPerPixel / 8);
                        int targetStart = targetStride * iy;

                        int length = w * sourceFormat.BitsPerPixel / 8;

                        Buffer.BlockCopy(source, sourceStart, target, targetStart, length);
                    }
                }
            }
        }
Ejemplo n.º 31
0
        /// <summary>
        /// Sends a rectangle using JPEG compression.
        /// </summary>
        /// <param name="stream">
        /// The <see cref="Stream"/> which represents connectivity with the client.
        /// </param>
        /// <param name="pixelFormat">
        /// The pixel format to use. This must be <see cref="VncPixelFormat.RGB32"/>.
        /// </param>
        /// <param name="region">
        /// The rectangle to send.
        /// </param>
        /// <param name="contents">
        /// A buffer holding the raw pixel data for the rectangle.
        /// </param>
        /// <param name="jpegQualityLevel">
        /// The JPEG quality level to use.
        /// </param>
        protected int SendWithJpegCompression(Stream stream, VncPixelFormat pixelFormat, VncRectangle region, byte[] contents, int jpegQualityLevel)
        {
            var subsamplingOption = TJSubsamplingOption.Chrominance420;

            var size = this.compressor.GetBufferSize(region.Width, region.Height, subsamplingOption) + 5;

            byte[] buffer = null;

            try
            {
                // The first 5 bytes will hold the compression control byte and the size of the
                // JPEG buffer.
                buffer = ArrayPool <byte> .Shared.Rent(size);

                var qualityLevel = GetQualityLevel(this.VncServerSession);

                var jpeg = this.compressor.Compress(
                    contents.AsSpan(),
                    buffer.AsSpan(5),
                    0, /* auto-calculate pitch */
                    region.Width,
                    region.Height,
                    TJPixelFormat.BGRA,
                    subsamplingOption,
                    jpegQualityLevel,
                    TJFlags.NoRealloc);

                // Write the JPEG compression control byte and the size of the JPEG buffer.
                buffer[0] = (byte)TightCompressionControl.JpegCompression;
                var length = WriteEncodedValue(buffer, 1, jpeg.Length);

                stream.Write(buffer, 0, length);
                stream.Write(buffer, 5, jpeg.Length);

                return(jpeg.Length + 5);
            }
            finally
            {
                if (buffer != null)
                {
                    ArrayPool <byte> .Shared.Return(buffer);
                }
            }
        }
Ejemplo n.º 32
0
 /// <summary>
 /// Sends the contents of a rectangle to the client.
 /// </summary>
 /// <param name="stream">
 /// A <see cref="Stream"/> which represents the connection to the VNC client.
 /// </param>
 /// <param name="pixelFormat">
 /// The <see cref="VncPixelFormat"/> being used.
 /// </param>
 /// <param name="region">
 /// The dimesions of the rectangle.
 /// </param>
 /// <param name="contents">
 /// The contents of the rectangle, in raw pixel format.
 /// </param>
 public abstract void Send(Stream stream, VncPixelFormat pixelFormat, VncRectangle region, byte[] contents);
 /// <summary>
 /// Initializes a new instance of the <see cref="FramebufferUpdateRequest"/> class.
 /// </summary>
 /// <param name="incremental">Whether an incremental update is desired.</param>
 /// <param name="region">The region to update.</param>
 public FramebufferUpdateRequest(bool incremental, VncRectangle region)
 {
     this.Incremental = incremental;
     this.Region = region;
 }
Ejemplo n.º 34
0
        /// <summary>
        /// Sends a rectangle using basic compression.
        /// </summary>
        /// <param name="stream">
        /// The <see cref="Stream"/> which represents connectivity with the client.
        /// </param>
        /// <param name="pixelFormat">
        /// The pixel format to use.
        /// </param>
        /// <param name="region">
        /// The rectangle to send.
        /// </param>
        /// <param name="contents">
        /// A buffer holding the raw pixel data for the rectangle.
        /// </param>
        protected int SendWithBasicCompression(Stream stream, VncPixelFormat pixelFormat, VncRectangle region, byte[] contents)
        {
            if (contents.Length < 12)
            {
                var compressionControl = TightCompressionControl.BasicCompression;
                stream.WriteByte((byte)compressionControl);

                // If the data size after applying the filter but before the compression is less then 12, then the data is sent as is, uncompressed.
                byte[] encodedBuffer = new byte[4];
                var    length        = WriteEncodedValue(encodedBuffer, 0, (int)contents.Length);
                stream.Write(encodedBuffer, 0, length);

                stream.Write(contents, 0, contents.Length);

                return(1 + length + contents.Length);
            }
            else
            {
                var compressionControl = TightCompressionControl.BasicCompression
                                         | TightCompressionControl.ResetStream0
                                         | TightCompressionControl.UseStream0;

                var compressionLevel = GetCompressionLevel(this.VncServerSession);

                stream.WriteByte((byte)compressionControl);

                using (var buffer = new MemoryStream())
                    using (var deflater = new ZlibStream(buffer, CompressionMode.Compress, compressionLevel))
                    {
                        // The Tight encoding makes use of a new type TPIXEL (Tight pixel). This is the same as a PIXEL for the agreed
                        // pixel format, except where true-colour-flag is non-zero, bits-per-pixel is 32, depth is 24 and all of the bits
                        // making up the red, green and blue intensities are exactly 8 bits wide.
                        // In this case a TPIXEL is only 3 bytes long, where the first byte is the red component, the second byte is the
                        // green component, and the third byte is the blue component of the pixel color value.
                        if (pixelFormat.BitsPerPixel == 32 &&
                            pixelFormat.BitDepth == 24 &&
                            pixelFormat.BlueBits == 8 &&
                            pixelFormat.RedBits == 8 &&
                            pixelFormat.GreenBits == 8 &&
                            !pixelFormat.IsPalettized)
                        {
                            Debug.Assert(contents.Length % 4 == 0, "The size of the raw pixel data must be a multiple of 4 when using a 32bpp pixel format.");

                            int redOffset   = pixelFormat.RedShift / 8;
                            int blueOffset  = pixelFormat.BlueShift / 8;
                            int greenOffset = pixelFormat.GreenShift / 8;

                            for (int i = 0; i < contents.Length; i += 4)
                            {
                                if (i == contents.Length - 4)
                                {
                                    deflater.FlushMode = FlushType.Full;
                                }

                                // The first byte is the red component, the second byte is the
                                // green component, and the third byte is the blue component of the pixel color value.
                                deflater.Write(contents, i + redOffset, 1);
                                deflater.Write(contents, i + greenOffset, 1);
                                deflater.Write(contents, i + blueOffset, 1);
                            }
                        }
                        else
                        {
                            deflater.FlushMode = FlushType.Finish;
                            deflater.Write(contents, 0, contents.Length);
                        }

                        deflater.Flush();

                        byte[] encodedBuffer = new byte[4];
                        var    length        = WriteEncodedValue(encodedBuffer, 0, (int)buffer.Length);
                        stream.Write(encodedBuffer, 0, length);

                        buffer.Position = 0;
                        buffer.CopyTo(stream);

                        return((int)buffer.Length + 1);
                    }
            }
        }