Beispiel #1
0
        private static unsafe Bitmap CreateAlphaBitmap(Bitmap srcBitmap, PixelFormat targetPixelFormat)
        {
            var result = new Bitmap(srcBitmap.Width, srcBitmap.Height, targetPixelFormat);

            var bmpBounds = new Rectangle(0, 0, srcBitmap.Width, srcBitmap.Height);
            var srcData   = srcBitmap.LockBits(bmpBounds, ImageLockMode.ReadOnly, srcBitmap.PixelFormat);
            var destData  = result.LockBits(bmpBounds, ImageLockMode.ReadOnly, targetPixelFormat);

            var srcDataPtr  = (byte *)srcData.Scan0;
            var destDataPtr = (byte *)destData.Scan0;

            try
            {
                for (int y = 0; y <= srcData.Height - 1; y++)
                {
                    for (int x = 0; x <= srcData.Width - 1; x++)
                    {
                        //this is really important because one stride may be positive and the other negative
                        var position  = srcData.Stride * y + 4 * x;
                        var position2 = destData.Stride * y + 4 * x;

                        CoreMemoryApi.memcpy(destDataPtr + position2, srcDataPtr + position, (UIntPtr)4);
                    }
                }
            }
            finally
            {
                srcBitmap.UnlockBits(srcData);
                result.UnlockBits(destData);
            }

            using (srcBitmap)
                return(result);
        }
        public void UpdateCursorImage(IntPtr data, int stride, int width, int height, PixelFormat pixelFormat)
        {
            if (CursorImage?.Width == width && CursorImage.Height == height && CursorImage.PixelFormat == pixelFormat)
            {
                var lockBits = CursorImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly,
                                                    pixelFormat);

                try
                {
                    if (CoreMemoryApi.memcmp(lockBits.Scan0, data, (UIntPtr)(stride * height)) == IntPtr.Zero)
                    {
                        return;
                    }
                }
                finally
                {
                    CursorImage.UnlockBits(lockBits);
                }
            }

            CursorImage = (Bitmap) new Bitmap(width, height, stride, pixelFormat, data).Clone();
            if (pixelFormat != PixelFormat.Format32bppArgb)
            {
                var oldImage = CursorImage;
                CursorImage = new Bitmap(CursorImage.Width, CursorImage.Height, PixelFormat.Format32bppArgb);
                using (var graphics = Graphics.FromImage(CursorImage))
                    graphics.DrawImageUnscaled(oldImage, 0, 0);
                oldImage.Dispose();
            }

            _imageInfo = new ImageInfo {
                Width = width, Height = height, PixelFormat = PixelFormat.Format32bppArgb
            };
            _cursorUpdated = true;
        }
Beispiel #3
0
        protected virtual unsafe RemoteDesktopDataInfo GetFullImageData(IntPtr scan0, Size imageSize, int stride, PixelFormat pixelFormat, int rawLength)
        {
            //just send the full image
            byte[] data;
            if ((ImageCompression.CompressionMode & CompressionMode.ByteArray) == CompressionMode.ByteArray)
            {
                data = ImageCompression.Compress(scan0, stride, imageSize, pixelFormat);
            }
            else if ((ImageCompression.CompressionMode & CompressionMode.Stream) == CompressionMode.Stream)
            {
                using (var ms = new MemoryStream(_encodeBuffer.Length / 200))
                {
                    ImageCompression.Compress(scan0, stride, imageSize, pixelFormat, ms);
                    data = ms.ToArray();
                }
            }
            else
            {
                throw new NotSupportedException(ImageCompression.CompressionMode.ToString());

                //Copy the image data to our byte array
                fixed(byte *ptr = _encodeBuffer)
                CoreMemoryApi.memcpy(new IntPtr(ptr), scan0, (UIntPtr)rawLength);

                return(new RemoteDesktopDataInfo(data,
                                                 new FrameInfo(new Rectangle(0, 0, imageSize.Width, imageSize.Height), FrameFlags.UpdatedRegion),
                                                 new HeaderInfo(ImageMetadata.FullImage, pixelFormat, imageSize.Width, imageSize.Height)));
        }
Beispiel #4
0
        public unsafe byte[] Decompress(IntPtr dataPtr, uint length, PixelFormat pixelFormat)
        {
            var buffer = new byte[length];

            fixed(byte *bufferPtr = buffer)
            CoreMemoryApi.memcpy(bufferPtr, (byte *)dataPtr, new UIntPtr(length));

            return(buffer);
        }
Beispiel #5
0
        public unsafe byte[] Compress(IntPtr scan0, int stride, Size imageSize, PixelFormat pixelFormat)
        {
            var data = new byte[stride * imageSize.Height];

            fixed(byte *dataPtr = data)
            CoreMemoryApi.memcpy(dataPtr, (byte *)scan0, (UIntPtr)data.Length);

            return(data);
        }
            /* public unsafe void Modify(IntPtr backBuffer, int stride, int pixelSize, Size size, List<Int32Rect> updatedAreas)
             * {
             *   if (_length < HeaderLength)
             *       return;
             *
             *   CursorInfo cursorInfo;
             *   ImageInfo imageInfo;
             *
             *   ReadHeader(_packet, _index, out cursorInfo, out imageInfo);
             *
             *   var cursorStride = imageInfo.Width * UnsafeStreamCodec.GetPixelSize(imageInfo.PixelFormat);
             *   var backBufferPtr = (byte*) backBuffer;
             *
             *   if (_length > HeaderLength)
             *   {
             *       _cursorStreamCodec.CursorPixelFormat = imageInfo.PixelFormat;
             *       fixed (byte* packetPointer = _packet)
             *       {
             *           if ((_cursorStreamCodec.ImageCompression.DecompressionMode & DecompressionMode.ByteArray) ==
             *               DecompressionMode.ByteArray)
             *           {
             *               var buffer =
             *                   _cursorStreamCodec.ImageCompression.Decompress(
             *                       (IntPtr) (packetPointer + _index + HeaderLength),
             *                       (uint) (_length - HeaderLength), imageInfo.PixelFormat);
             *
             *               fixed (byte* bufferPtr = buffer)
             *                   using (
             *                       var bitmap = new Bitmap(imageInfo.Width, imageInfo.Height, cursorStride,
             *                           imageInfo.PixelFormat, (IntPtr) bufferPtr))
             *                   {
             *                       _cursorStreamCodec.CursorImage =
             *                           bitmap.Clone(new Rectangle(0, 0, imageInfo.Width, imageInfo.Height),
             *                               imageInfo.PixelFormat);
             *                   }
             *           }
             *           else if ((_cursorStreamCodec.ImageCompression.DecompressionMode & DecompressionMode.Bitmap) ==
             *                    DecompressionMode.Bitmap)
             *           {
             *               byte[] temp = new byte[_length - HeaderLength];
             *               fixed (byte* tempPtr = temp)
             *                   NativeMethods.memcpy(tempPtr, packetPointer + _index + HeaderLength,
             *                       (UIntPtr) temp.Length);
             *
             *               using (var memoryStream = new MemoryStream(temp))
             *               {
             *                   _cursorStreamCodec.CursorImage = (Bitmap) Image.FromStream(memoryStream).Clone();
             *               }
             *           }
             *           else
             *               throw new NotSupportedException(
             *                   _cursorStreamCodec.ImageCompression.DecompressionMode.ToString());
             *       }
             *   }
             *
             *   RestoreImage(updatedAreas, backBufferPtr, stride, pixelSize);
             *   if (cursorInfo.Visible && _cursorStreamCodec.CursorImage != null)
             *   {
             *       var cursorRect = new Int32Rect(cursorInfo.X, cursorInfo.Y, imageInfo.Width,
             *           imageInfo.Height);
             *
             *       _cursorStreamCodec.ModifiedAreaRectangle = cursorRect;
             *       _cursorStreamCodec.UnmodifiedAreaData = new byte[pixelSize * imageInfo.Width * imageInfo.Height];
             *
             *       fixed (byte* bufferPtr = _cursorStreamCodec.UnmodifiedAreaData)
             *           WriteArea(cursorRect, backBufferPtr, stride, pixelSize, bufferPtr);
             *
             *       using (var bitmap = new Bitmap(imageInfo.Width, imageInfo.Height, imageInfo.PixelFormat))
             *       {
             *           var lockBits = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
             *               ImageLockMode.ReadWrite, bitmap.PixelFormat);
             *
             *           WriteArea(cursorRect, backBufferPtr, stride, pixelSize, (byte*) lockBits.Scan0);
             *           bitmap.UnlockBits(lockBits);
             *
             *           using (var graphics = Graphics.FromImage(bitmap))
             *           {
             *               graphics.DrawImageUnscaled(_cursorStreamCodec.CursorImage, new System.Drawing.Point(0, 0));
             *           }
             *
             *           lockBits = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
             *               ImageLockMode.ReadWrite, bitmap.PixelFormat);
             *
             *           var imagePtr = (byte*) lockBits.Scan0;
             *           for (int i = 0; i < cursorRect.Height; i++)
             *           {
             *               NativeMethods.memcpy(backBufferPtr + (i + cursorRect.Y) * stride + cursorRect.X * pixelSize,
             *                   imagePtr + i * lockBits.Stride,
             *                   (UIntPtr) lockBits.Stride);
             *           }
             *
             *           bitmap.UnlockBits(lockBits);
             *       }
             *
             *       updatedAreas.Add(cursorRect);
             *   }
             * }*/

            private unsafe void WriteArea(Int32Rect rectangle, byte *backBuffer, int backBufferStride, int pixelSize,
                                          byte *destination)
            {
                var rectangleStride = rectangle.Width * pixelSize;

                for (int i = 0; i < rectangle.Height; i++)
                {
                    CoreMemoryApi.memcpy(destination + i * rectangleStride,
                                         backBuffer + (i + rectangle.Y) * backBufferStride + rectangle.X * pixelSize,
                                         (UIntPtr)rectangleStride);
                }
            }
            /* private unsafe void RestoreImage(List<Int32Rect> updatedAreas, byte* backBuffer, int stride, int pixelSize)
             * {
             *   if (_cursorStreamCodec.UnmodifiedAreaData == null)
             *       return;
             *
             *   //we dont replace the area when it was updated
             *   foreach (var updatedArea in updatedAreas)
             *   {
             *       if (updatedArea.Contains(_cursorStreamCodec.ModifiedAreaRectangle))
             *           return;
             *   }
             *
             *   var rectangle = _cursorStreamCodec.ModifiedAreaRectangle;
             *   var rectangleStride = rectangle.Width * pixelSize;
             *
             *   //copy unmodified data to backbuffer
             *   fixed (byte* unmodifiedData = _cursorStreamCodec.UnmodifiedAreaData)
             *   {
             *       for (int i = 0; i < rectangle.Height; i++)
             *       {
             *           NativeMethods.memcpy(backBuffer + (rectangle.Y + i) * stride + rectangle.X * pixelSize,
             *               unmodifiedData + i * rectangleStride, (UIntPtr) rectangleStride);
             *       }
             *   }
             *
             *   updatedAreas.Add(rectangle);
             *   _cursorStreamCodec.UnmodifiedAreaData = null;
             * }*/

            public unsafe void PreProcessing(IntPtr backBuffer, int stride, int pixelSize, Size size,
                                             List <Int32Rect> updatedAreas)
            {
                if (_cursorStreamCodec.RestoreUnmodifiedImage)
                {
                    fixed(byte *unmodifiedImagePtr = _cursorStreamCodec.UnmodifiedImage)
                    {
                        CoreMemoryApi.memcpy((byte *)backBuffer, unmodifiedImagePtr,
                                             (UIntPtr)_cursorStreamCodec.UnmodifiedImage.Length);
                    }

                    updatedAreas.Add(_cursorStreamCodec.ModifiedAreaRectangle);
                }
            }
        public void UpdateCursorImage(Bitmap bitmap)
        {
            if (CursorImage == null)
            {
                CursorImage = bitmap;
            }
            else
            {
                if (CursorImage.Width == bitmap.Width && CursorImage.Height == bitmap.Height &&
                    CursorImage.PixelFormat == bitmap.PixelFormat)
                {
                    var newImageLock = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
                                                       ImageLockMode.ReadOnly, bitmap.PixelFormat);
                    var currentImageLock = CursorImage.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
                                                                ImageLockMode.ReadOnly, bitmap.PixelFormat);

                    bool imagesEqual = false;
                    if (newImageLock.Stride == currentImageLock.Stride)
                    {
                        var imageSize = newImageLock.Stride * newImageLock.Height;

                        imagesEqual =
                            CoreMemoryApi.memcmp(newImageLock.Scan0, currentImageLock.Scan0, (UIntPtr)imageSize) ==
                            IntPtr.Zero;
                    }

                    bitmap.UnlockBits(newImageLock);
                    CursorImage.UnlockBits(currentImageLock);

                    if (imagesEqual)
                    {
                        bitmap.Dispose();
                        return;
                    }
                }

                CursorImage = bitmap;
            }

            _imageInfo = new ImageInfo {
                Width = bitmap.Width, Height = bitmap.Height, PixelFormat = bitmap.PixelFormat
            };
            _cursorUpdated = true;
        }
            public unsafe void PostProcessing(IntPtr backBuffer, int stride, int pixelSize, Size size,
                                              List <Int32Rect> updatedAreas)
            {
                CursorInfo cursorInfo;
                ImageInfo  imageInfo;

                ReadHeader(_packet, _index, out cursorInfo, out imageInfo);

                var backBufferPtr = (byte *)backBuffer;

                if (_length > HeaderLength)
                {
                    _cursorStreamCodec.CursorImage?.Dispose();
                    _cursorStreamCodec.CursorMemoryStream?.Dispose();

                    _cursorStreamCodec.CursorPixelFormat  = imageInfo.PixelFormat;
                    _cursorStreamCodec.CursorMemoryStream = new MemoryStream(_packet, _index + HeaderLength,
                                                                             _length - HeaderLength);
                    _cursorStreamCodec.CursorImage = (Bitmap)Image.FromStream(_cursorStreamCodec.CursorMemoryStream);
                }

                if (cursorInfo.Visible)
                {
                    var cursorRect = new Int32Rect(cursorInfo.X, cursorInfo.Y,
                                                   Math.Min(imageInfo.Width, size.Width - cursorInfo.X),
                                                   Math.Min(imageInfo.Height, size.Height - cursorInfo.Y));
                    _cursorStreamCodec.ModifiedAreaRectangle = cursorRect;

                    //backup original image without cursor so we can restore it in pre processing
                    var imageLength = stride * size.Height;
                    if (_cursorStreamCodec.UnmodifiedImage?.Length != imageLength)
                        _cursorStreamCodec.UnmodifiedImage = new byte[imageLength];

                    fixed(byte *unmodifiedPtr = _cursorStreamCodec.UnmodifiedImage)
                    CoreMemoryApi.memcpy(unmodifiedPtr, (byte *)backBuffer, (UIntPtr)imageLength);

                    using (var bitmap = new Bitmap(imageInfo.Width, imageInfo.Height, imageInfo.PixelFormat))
                    {
                        var lockBits = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
                                                       ImageLockMode.ReadWrite, bitmap.PixelFormat);

                        WriteArea(cursorRect, backBufferPtr, stride, pixelSize, (byte *)lockBits.Scan0);
                        bitmap.UnlockBits(lockBits);

                        using (var graphics = Graphics.FromImage(bitmap))
                        {
                            graphics.DrawImageUnscaled(_cursorStreamCodec.CursorImage, new System.Drawing.Point(0, 0));
                        }

                        lockBits = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
                                                   ImageLockMode.ReadWrite, bitmap.PixelFormat);

                        var imagePtr        = (byte *)lockBits.Scan0;
                        var rectangleStride = cursorRect.Width * pixelSize;

                        for (int i = 0; i < cursorRect.Height; i++)
                        {
                            CoreMemoryApi.memcpy(backBufferPtr + (i + cursorRect.Y) * stride + cursorRect.X * pixelSize,
                                                 imagePtr + i * lockBits.Stride,
                                                 (UIntPtr)rectangleStride);
                        }

                        bitmap.UnlockBits(lockBits);

                        updatedAreas.Add(cursorRect);
                        _cursorStreamCodec.ModifiedAreaRectangle  = cursorRect;
                        _cursorStreamCodec.RestoreUnmodifiedImage = true;
                    }
                }
                else
                {
                    _cursorStreamCodec.RestoreUnmodifiedImage = false;
                }
            }
Beispiel #10
0
 public void Decompress(IntPtr dataPtr, uint length, IntPtr outputPtr, int outputLength, PixelFormat pixelFormat)
 {
     CoreMemoryApi.memcpy(outputPtr, dataPtr, new UIntPtr(length));
 }
Beispiel #11
0
        public unsafe RemoteDesktopDataInfo CodeImage(IntPtr scan0, Rectangle scanArea, Size imageSize, PixelFormat pixelFormat)
        {
            var pixelSize = GetPixelSize(pixelFormat);
            var stride    = pixelSize * imageSize.Width;
            var rawLength = stride * imageSize.Height;

            if (_encodeBuffer == null || (_sendFullImage && _fullImageStopwatch.ElapsedMilliseconds > FullImageTime))
            {
                _encodeBuffer = new byte[rawLength];
                _stride       = stride;

                return(GetFullImageData(scan0, imageSize, stride, pixelFormat, rawLength));
            }

            if (stride != _stride)
            {
                throw new InvalidOperationException("Image size is not equal to previous Bitmap");
            }

            //the block lines which need an update
            var blocks = new List <Rectangle>();

            //the current stride
            var s = new Size(scanArea.Width, _checkBlock.Height);

            //the last size to scan
            var lastSize = new Size(scanArea.Width % _checkBlock.Width, scanArea.Height % _checkBlock.Height);

            var lastY = scanArea.Height + scanArea.Y - lastSize.Height;
            var lastX = scanArea.Width + scanArea.X - lastSize.Width;

            //different blocks but all blocks have the static width of scanArea.Width

            var finalUpdates = new List <Rectangle>();

            byte *pScan0 = (byte *)scan0.ToPointer();

            fixed(byte *encBuffer = _encodeBuffer)
            {
                int index;

                //loop until the end of the scan area is reached (y)
                Rectangle cBlock;

                for (int y = scanArea.Y; scanArea.Height + scanArea.Y >= y;)
                {
                    if (y == lastY)
                    {
                        s = new Size(scanArea.Width, lastSize.Height);
                    }

                    if (s.Height == 0)
                    {
                        break;
                    }

                    cBlock = new Rectangle(scanArea.X, y, scanArea.Width, s.Height);
                    var offset = y * stride + scanArea.X * pixelSize;

                    //if the byte arrays are different
                    if (
                        CoreMemoryApi.memcmp(encBuffer + offset, pScan0 + offset, (UIntPtr)(scanArea.Width * pixelSize)) !=
                        IntPtr.Zero)
                    {
                        //get the last block index
                        index = blocks.Count - 1;
                        //if the last block index is directly above the current one, we just add the stride to it
                        if (blocks.Count != 0 && blocks[index].Y + blocks[index].Height == cBlock.Y)
                        {
                            cBlock = new Rectangle(blocks[index].X, blocks[index].Y, blocks[index].Width,
                                                   blocks[index].Height + cBlock.Height);
                            blocks[index] = cBlock;
                        }
                        else
                        {
                            //else we just add a new block
                            blocks.Add(cBlock);
                        }
                    }

                    y += s.Height;
                }

                //we go through all blocks
                for (var i = 0; i < blocks.Count; i++)
                {
                    //we set the height to the current block height but the width to the check block width
                    s = new Size(_checkBlock.Width, blocks[i].Height);
                    var x = scanArea.X;

                    //we loop in steps of checkblock.width to the end of the line
                    while (x < (scanArea.Width + scanArea.X))
                    {
                        if (x == lastX)
                        {
                            s = new Size(lastSize.Width, blocks[i].Height);
                        }

                        //the block has the height of the saved block but the width of the check block
                        cBlock = new Rectangle(x, blocks[i].Y, s.Width, blocks[i].Height);
                        bool foundChanges = false;
                        int  blockStride  = pixelSize * cBlock.Width;

                        //we loop throught the strides of this block
                        for (int j = 0; j < cBlock.Height; j++)
                        {
                            int blockOffset = stride * (cBlock.Y + j) + pixelSize * cBlock.X;
                            if (
                                CoreMemoryApi.memcmp(encBuffer + blockOffset, pScan0 + blockOffset,
                                                     (UIntPtr)blockStride) != IntPtr.Zero)
                            {
                                foundChanges = true;
                            }

                            //we copy always because if foundChanges = true we have to invalidate the full block
                            CoreMemoryApi.memcpy(encBuffer + blockOffset, pScan0 + blockOffset, (UIntPtr)blockStride);
                        }

                        if (foundChanges)
                        {
                            index = finalUpdates.Count - 1;

                            //if the last addded block ends where this block begins
                            if (finalUpdates.Count > 0 && finalUpdates[index].X + finalUpdates[index].Width == cBlock.X)
                            {
                                //we just add the width
                                Rectangle rect     = finalUpdates[index];
                                int       newWidth = cBlock.Width + rect.Width;
                                cBlock = new Rectangle(rect.X, rect.Y, newWidth, rect.Height);
                                finalUpdates[index] = cBlock;
                            }
                            else
                            {
                                //else add a new block
                                finalUpdates.Add(cBlock);
                            }
                        }
                        x += s.Width;
                    }
                }

                return(WriteChanges(finalUpdates.ToArray(), null, pixelSize, encBuffer, pixelFormat, imageSize, stride));
            }
        }
Beispiel #12
0
        protected virtual unsafe RemoteDesktopDataInfo WriteChanges(Rectangle[] changedAreas, MovedRegion[] movedRegions,
                                                                    int pixelSize, byte *imageBuffer, PixelFormat pixelFormat, Size imageSize, int imageStride)
        {
            MemoryStream memoryStream = null;

            byte[][] dataArrays         = null;
            var      changedAreasLength = changedAreas?.Length ?? 0;
            var      movedRegionsLength = movedRegions?.Length ?? 0;

            FrameInfo[] frames = null;

            if ((ImageCompression.CompressionMode & CompressionMode.Stream) == CompressionMode.Stream)
            {
                memoryStream = new MemoryStream(_encodeBuffer.Length / 200);
                RemoteDesktopDataInfo.WriteHeader(pixelFormat, ImageMetadata.Frames, imageSize.Width, imageSize.Height, memoryStream);
            }
            else if ((ImageCompression.CompressionMode & CompressionMode.ByteArray) == CompressionMode.ByteArray)
            {
                dataArrays = new byte[changedAreasLength + movedRegionsLength][];
                frames     = new FrameInfo[changedAreasLength + movedRegionsLength];
            }
            else
            {
                throw new NotSupportedException(ImageCompression.CompressionMode.ToString());
            }

            if (movedRegions != null)
            {
                for (int i = 0; i < movedRegions.Length; i++)
                {
                    var movedRegion = movedRegions[i];

                    if (memoryStream != null)
                    {
                        RemoteDesktopDataInfo.WriteFrameInfo(movedRegion.Destination, 8, FrameFlags.MovedRegion, memoryStream);

                        memoryStream.Write(BitConverter.GetBytes(movedRegion.Source.X), 0, 4);
                        memoryStream.Write(BitConverter.GetBytes(movedRegion.Source.Y), 0, 4);
                    }
                    else
                    {
                        var data = new byte[8];
                        Buffer.BlockCopy(BitConverter.GetBytes(movedRegion.Source.X), 0, data, 0, 4);
                        Buffer.BlockCopy(BitConverter.GetBytes(movedRegion.Source.Y), 0, data, 4, 4);

                        dataArrays[i] = data;
                        frames[i]     = new FrameInfo(movedRegion.Destination, FrameFlags.MovedRegion);
                    }
                }
            }

            if (changedAreas != null)
            {
                for (int i = 0; i < changedAreas.Length; i++)
                {
                    var blockArea    = changedAreas[i];
                    var blockStride  = blockArea.Width * pixelSize;
                    var blockPointer = Marshal.AllocHGlobal(blockStride * blockArea.Height);

                    try
                    {
                        //build bitmap in memory
                        for (int j = 0, offset = 0; j < blockArea.Height; j++)
                        {
                            int blockOffset = imageStride * (blockArea.Y + j) + pixelSize * blockArea.X;
                            CoreMemoryApi.memcpy(blockPointer.Add(offset), (IntPtr)(imageBuffer + blockOffset),
                                                 (UIntPtr)blockStride);
                            offset += blockStride;
                        }

                        if (memoryStream != null)
                        {
                            RemoteDesktopDataInfo.WriteFrameInfo(blockArea, 0, FrameFlags.UpdatedRegion, memoryStream);
                            var framePos = memoryStream.Position;
                            ImageCompression.Compress(blockPointer, blockStride, blockArea.Size, pixelFormat,
                                                      memoryStream);

                            var currentPos = memoryStream.Position;
                            memoryStream.Position = framePos - 4;
                            memoryStream.Write(BitConverter.GetBytes(currentPos - framePos), 0, 4);
                            memoryStream.Position = currentPos;
                        }
                        else
                        {
                            dataArrays[i + movedRegionsLength] = ImageCompression.Compress(blockPointer, blockStride,
                                                                                           blockArea.Size, pixelFormat);
                            frames[i + movedRegionsLength] = new FrameInfo(blockArea, FrameFlags.UpdatedRegion);
                        }
                    }
                    finally
                    {
                        Marshal.FreeHGlobal(blockPointer);
                    }
                }
            }

            if (memoryStream != null)
            {
                using (memoryStream)
                    return(new RemoteDesktopDataInfo(memoryStream.ToArray()));
            }

            return(new RemoteDesktopDataInfo(dataArrays, frames,
                                             new HeaderInfo(ImageMetadata.Frames, pixelFormat, imageSize.Width, imageSize.Height)));
        }
        private void RenderApplications()
        {
            if (Windows.Count == 0 || Windows.All(x => x.Image == null))
            {
                ApplicationsBitmap = null;
                return;
            }

            var imageX = Windows.Min(x => x.X);
            var imageY = Windows.Min(x => x.Y);

            var height = 0;
            var width  = 0;

            IntPtr backBuffer       = IntPtr.Zero;
            int    backBufferStride = 0;

            var renderWindowInfos = new List <WindowRenderInfo>();

            Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(() =>
            {
                height = Windows.Max(x => x.Y + x.Image?.PixelHeight ?? 0) - imageY;
                width  = Windows.Max(x => x.X + x.Image?.PixelWidth ?? 0) - imageX;

                if (ApplicationsBitmap == null || _applicationsBitmapHeight != height || _applicationsBitmapWidth != width)
                {
                    ApplicationsBitmap        = new WriteableBitmap(width, height, 96, 96, PixelFormats.Bgr32, null);
                    _applicationsBitmapHeight = height;
                    _applicationsBitmapWidth  = width;
                }

                ApplicationsBitmap.Lock();
                backBuffer       = ApplicationsBitmap.BackBuffer;
                backBufferStride = ApplicationsBitmap.BackBufferStride;

                foreach (var window in Windows)
                {
                    if (window.Image == null)
                    {
                        continue;
                    }

                    renderWindowInfos.Add(new WindowRenderInfo(window));
                }
            })).Wait();

            //important: always update, regardless if the image size was changed
            _applicationBitmapLeft = imageX;
            _applicationBitmapTop  = imageY;

            var pixelSize   = 4;
            var imageLength = backBufferStride * _applicationsBitmapHeight;

            CoreMemoryApi.memset(backBuffer, 0, (UIntPtr)imageLength);

            unsafe
            {
                var applicationImagePtr = (byte *)backBuffer;

                foreach (var renderWindowInfo in renderWindowInfos)
                {
                    var windowX = renderWindowInfo.RenderWindow.X - imageX;
                    var windowY = renderWindowInfo.RenderWindow.Y - imageY;

                    fixed(byte *bufferPtr = renderWindowInfo.Buffer)
                    for (int j = 0; j < renderWindowInfo.Height; j++)
                    {
                        var positionImage = (windowY + j) * backBufferStride + windowX * pixelSize;

                        CoreMemoryApi.memcpy(applicationImagePtr + positionImage,
                                             bufferPtr + j * renderWindowInfo.Stride,
                                             (UIntPtr)renderWindowInfo.Stride);
                    }
                }
            }

            Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(() =>
            {
                ApplicationsBitmap.AddDirtyRect(new Int32Rect(0, 0, width, height));
                ApplicationsBitmap.Unlock();
            })).Wait();
        }