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; }
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))); }
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); }
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; } }
public void Decompress(IntPtr dataPtr, uint length, IntPtr outputPtr, int outputLength, PixelFormat pixelFormat) { CoreMemoryApi.memcpy(outputPtr, dataPtr, new UIntPtr(length)); }
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)); } }
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(); }