void NegotiateDesktop()
        {
            byte shareDesktopSetting = _c.ReceiveByte();//是否同意分享屏幕
            bool shareDesktop        = shareDesktopSetting != 0;

            var e = new CreatingDesktopEventArgs(shareDesktop);

            OnCreatingDesktop(e);

            var fbSource = _fbSource;

            Framebuffer = fbSource != null?fbSource.Capture() : null;

            VncStream.Require(Framebuffer != null,
                              "No framebuffer. Make sure you've called SetFramebufferSource. It can be set to a VncFramebuffer.",
                              VncFailureReason.SanityCheckFailed);
            _clientPixelFormat = Framebuffer.PixelFormat;
            _clientWidth       = Framebuffer.Width; _clientHeight = Framebuffer.Height;
            _fbuAutoCache      = null;

            _c.SendUInt16BE((ushort)Framebuffer.Width);
            _c.SendUInt16BE((ushort)Framebuffer.Height);
            var pixelFormat = new byte[VncPixelFormat.Size];

            Framebuffer.PixelFormat.Encode(pixelFormat, 0);
            _c.Send(pixelFormat);
            _c.SendString(Framebuffer.Name, true);
        }
Example #2
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);
        }
Example #3
0
        private void NegotiateDesktop()
        {
            this.logger?.Log(LogLevel.Info, () => "Negotiating desktop settings");

            byte shareDesktopSetting = this.c.ReceiveByte();
            bool shareDesktop        = shareDesktopSetting != 0;

            var e = new CreatingDesktopEventArgs(shareDesktop);

            this.OnCreatingDesktop(e);

            var fbSource = this.fbSource;

            this.Framebuffer = fbSource != null?fbSource.Capture() : null;

            VncStream.Require(
                this.Framebuffer != null,
                "No framebuffer. Make sure you've called SetFramebufferSource. It can be set to a VncFramebuffer.",
                VncFailureReason.SanityCheckFailed);
            this.clientPixelFormat = this.Framebuffer.PixelFormat;
            this.clientWidth       = this.Framebuffer.Width;
            this.clientHeight      = this.Framebuffer.Height;
            this.fbuAutoCache      = null;

            this.c.SendUInt16BE((ushort)this.Framebuffer.Width);
            this.c.SendUInt16BE((ushort)this.Framebuffer.Height);
            var pixelFormat = new byte[VncPixelFormat.Size];

            this.Framebuffer.PixelFormat.Encode(pixelFormat, 0);
            this.c.Send(pixelFormat);
            this.c.SendString(this.Framebuffer.Name, true);

            this.logger?.Log(LogLevel.Info, () => $"The desktop {this.Framebuffer.Name} has initialized with pixel format {this.clientPixelFormat}; the screen size is {this.clientWidth}x{this.clientHeight}");
        }
Example #4
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);
            }
        }
Example #5
0
        public void DecodeTest()
        {
            var buffer      = new byte[] { 32, 24, 0, 1, 0, 255, 0, 255, 0, 255, 16, 8, 0, 0, 0, 0 };
            var pixelFormat = VncPixelFormat.Decode(buffer, 0);

            Assert.Equal(VncPixelFormat.RGB32, pixelFormat);
        }
Example #6
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);
            }
        }
        void HandleSetPixelFormat()
        {
            _c.Receive(3);

            var pixelFormat = _c.Receive(VncPixelFormat.Size);

            _clientPixelFormat = VncPixelFormat.Decode(pixelFormat, 0);
        }
Example #8
0
        private void HandleSetPixelFormat()
        {
            this.c.Receive(3);

            var pixelFormat = this.c.Receive(VncPixelFormat.Size);

            this.clientPixelFormat = VncPixelFormat.Decode(pixelFormat, 0);
        }
Example #9
0
        private void UpdateServerFormat(VncPixelFormat pixelFormat)
        {
            var serverFormat = this.server.ServerFormat;

            serverFormat.RedShift   = (byte)pixelFormat.RedShift;
            serverFormat.GreenShift = (byte)pixelFormat.GreenShift;
            serverFormat.BlueShift  = (byte)pixelFormat.BlueShift;

            this.server.ServerFormat = serverFormat;
        }
        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());
        }
Example #11
0
        public void EncodeTest()
        {
            var pixelFormat = new VncPixelFormat();
            var buffer      = new byte[VncPixelFormat.Size];

            pixelFormat.Encode(buffer, 0);

            var expected = new byte[] { 32, 24, 0, 1, 0, 255, 0, 255, 0, 255, 16, 8, 0, 0, 0, 0 };

            Assert.Equal(expected, buffer);
        }
Example #12
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);
        }
Example #13
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);
            }
        }
Example #14
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);
            }
        }
Example #15
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);
                }
            }
        }
Example #16
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));
            }
        }
Example #17
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);
            }
        }
Example #18
0
        public void ConstructorTest()
        {
            // Default pixel format should be RGB32.
            var pixelFormat = new VncPixelFormat();

            Assert.Equal(24, pixelFormat.BitDepth);
            Assert.Equal(32, pixelFormat.BitsPerPixel);
            Assert.Equal(4, pixelFormat.BytesPerPixel);

            Assert.True(pixelFormat.IsLittleEndian);
            Assert.False(pixelFormat.IsPalettized);

            Assert.Equal(8, pixelFormat.BlueBits);
            Assert.Equal(255, pixelFormat.BlueMax);
            Assert.Equal(0, pixelFormat.BlueShift);

            Assert.Equal(8, pixelFormat.GreenBits);
            Assert.Equal(255, pixelFormat.GreenMax);
            Assert.Equal(8, pixelFormat.GreenShift);

            Assert.Equal(8, pixelFormat.RedBits);
            Assert.Equal(255, pixelFormat.RedMax);
            Assert.Equal(16, pixelFormat.RedShift);
        }
Example #19
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);
            }
        }
        private void HandleSetPixelFormat()
        {
            this.c.Receive(3);

            var pixelFormat = this.c.Receive(VncPixelFormat.Size);
            this.clientPixelFormat = VncPixelFormat.Decode(pixelFormat, 0);
        }
        private void NegotiateDesktop()
        {
            Logger.Info("Negotiating desktop settings");

            byte shareDesktopSetting = this.c.ReceiveByte();
            bool shareDesktop = shareDesktopSetting != 0;

            var e = new CreatingDesktopEventArgs(shareDesktop);
            this.OnCreatingDesktop(e);

            var fbSource = this.fbSource;
            this.Framebuffer = fbSource != null ? fbSource.Capture() : null;
            VncStream.Require(
                this.Framebuffer != null,
                              "No framebuffer. Make sure you've called SetFramebufferSource. It can be set to a VncFramebuffer.",
                              VncFailureReason.SanityCheckFailed);
            this.clientPixelFormat = this.Framebuffer.PixelFormat;
            this.clientWidth = this.Framebuffer.Width;
            this.clientHeight = this.Framebuffer.Height;
            this.fbuAutoCache = null;

            this.c.SendUInt16BE((ushort)this.Framebuffer.Width);
            this.c.SendUInt16BE((ushort)this.Framebuffer.Height);
            var pixelFormat = new byte[VncPixelFormat.Size];
            this.Framebuffer.PixelFormat.Encode(pixelFormat, 0);
            this.c.Send(pixelFormat);
            this.c.SendString(this.Framebuffer.Name, true);

            Logger.Info($"The desktop {this.Framebuffer.Name} has initialized with pixel format {this.clientPixelFormat}; the screen size is {this.clientWidth}x{this.clientHeight}");
        }
Example #22
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);
Example #23
0
 /// <inheritdoc/>
 public override void Send(Stream stream, VncPixelFormat pixelFormat, VncRectangle region, byte[] contents)
 {
     stream.Write(contents, 0, contents.Length);
 }
Example #24
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);
                    }
            }
        }