using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.Linq; using System.Text; using System.Threading.Tasks; using Tango.RemoteDesktop.Frames; namespace Tango.RemoteDesktop.Comparers { /// /// Represents a raster comparer which will compare and return a difference. /// /// public class RasterBitmapComparer : IBitmapComparer { /// /// When max number of differences reached, the comparer will immediately return the current difference frame. /// This should enforce the screen capture engine to report 'No Difference Available'. /// public long? MaxDifferencesThrow { get; set; } /// /// Creates the difference as . /// /// The previous bitmap. /// The current bitmap. /// /// /// Cannot compare image. They are the same instance /// or /// Cannot compare image of different size. /// unsafe public BitmapComparerResult CreateDifference(Bitmap previousBitmap, Bitmap currentBitmap) { if (previousBitmap == null | currentBitmap == null) throw new InvalidOperationException("Cannot compare image. They are the same instance"); if (previousBitmap.Height != currentBitmap.Height || previousBitmap.Width != currentBitmap.Width) throw new InvalidOperationException("Cannot compare image of different size."); uint count = 0; Color matchColor = Color.Transparent; Bitmap diffImage = currentBitmap.Clone() as Bitmap; int height = previousBitmap.Height; int width = previousBitmap.Width; BitmapData data1 = previousBitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); BitmapData data2 = currentBitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); BitmapData diffData = diffImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); byte* data1Ptr = (byte*)data1.Scan0; byte* data2Ptr = (byte*)data2.Scan0; byte* diffPtr = (byte*)diffData.Scan0; byte[] swapColor = new byte[4]; swapColor[0] = matchColor.B; swapColor[1] = matchColor.G; swapColor[2] = matchColor.R; swapColor[3] = matchColor.A; int rowPadding = data1.Stride - (previousBitmap.Width * 4); // iterate over height (rows) for (int i = 0; i < height; i++) { // iterate over width (columns) for (int j = 0; j < width; j++) { int same = 0; byte[] tmp = new byte[4]; // compare pixels and copy new values into temporary array for (int x = 0; x < 4; x++) { tmp[x] = data2Ptr[0]; if (data1Ptr[0] == data2Ptr[0]) { same++; } data1Ptr++; // advance image1 ptr data2Ptr++; // advance image2 ptr } // swap color or add new values for (int x = 0; x < 4; x++) { diffPtr[0] = (same == 4) ? swapColor[x] : tmp[x]; diffPtr++; // advance diff image ptr } if (same != 4) { count++; if (MaxDifferencesThrow != null && count > MaxDifferencesThrow.Value) { previousBitmap.UnlockBits(data1); currentBitmap.UnlockBits(data2); diffImage.UnlockBits(diffData); diffImage.Dispose(); throw new MaxDifferencesReachedException(); } } } // at the end of each column, skip extra padding if (rowPadding > 0) { data1Ptr += rowPadding; data2Ptr += rowPadding; diffPtr += rowPadding; } } previousBitmap.UnlockBits(data1); currentBitmap.UnlockBits(data2); diffImage.UnlockBits(diffData); return new BitmapComparerResult() { Frame = new RasterFrame(diffImage), DifferenceCount = count }; } } }