public void Merge (DiffBounds other)
			{
				this.left = System.Math.Min (this.left, other.left);
				this.right = System.Math.Max (this.right, other.right);
				this.top = System.Math.Min (this.top, other.top);
				this.bottom = System.Math.Max (this.bottom, other.bottom);
			}
 public void Merge(DiffBounds other)
 {
     this.left   = System.Math.Min(this.left, other.left);
     this.right  = System.Math.Max(this.right, other.right);
     this.top    = System.Math.Min(this.top, other.top);
     this.bottom = System.Math.Max(this.bottom, other.bottom);
 }
		public static unsafe SurfaceDiff Create (ISurface original, ISurface updated_surf, bool force = false)
		{
			if (original.Width != updated_surf.Width || original.Height != updated_surf.Height) {
				// If the surface changed size, only throw an error if the user forced the use of a diff.
				if (force) {
					throw new InvalidOperationException ("SurfaceDiff requires surfaces to be same size.");
				} else {
					return null;
				}
			}

			// Cache some pinvokes
			var orig_width = original.Width;
			var orig_height = original.Height;

#if DEBUG_DIFF
			Console.WriteLine ("Original surface size: {0}x{1}", orig_width, orig_height);
			System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch();
			timer.Start();
#endif

			// STEP 1 - Find the bounds of the changed pixels.
			var orig_ptr = (int*)original.GetRowAddress (0);
			var updated_ptr = (int*)updated_surf.GetRowAddress (0);

			DiffBounds diff_bounds = new DiffBounds (orig_width, orig_height);
			object diff_bounds_lock = new Object();

			// Split up the work among several threads, each of which processes one row at a time
			// and updates the bounds of the changed pixels it has seen so far. At the end, the
			// results from each thread are merged together to find the overall bounds of the changed
			// pixels.
			Parallel.For<DiffBounds>(0, orig_height, () => new DiffBounds (orig_width, orig_height),
                     		(row, loop, my_bounds) => {

					var offset = row * orig_width;
					var orig = orig_ptr + offset;
					var updated = updated_ptr + offset;
					bool change_in_row = false;

					for (int i = 0; i < orig_width; ++i) {
						if (*(orig++) != *(updated++)) {
							change_in_row = true;
							my_bounds.left = System.Math.Min(my_bounds.left, i);
							my_bounds.right = System.Math.Max(my_bounds.right, i);
						}				
					}

					if (change_in_row) {
						my_bounds.top = System.Math.Min(my_bounds.top, row);
						my_bounds.bottom = System.Math.Max(my_bounds.bottom, row);
					}

					return my_bounds;

			},	(my_bounds) => {
					lock (diff_bounds_lock) {
						diff_bounds.Merge (my_bounds);
					}
					return;
			});

			var bounds = new Rectangle (diff_bounds.left, diff_bounds.top,
			                                diff_bounds.right - diff_bounds.left + 1,
			                                diff_bounds.bottom - diff_bounds.top + 1);

#if DEBUG_DIFF
			Console.WriteLine ("Truncated surface size: {0}x{1}", bounds.Width, bounds.Height);
#endif

			// STEP 2 - Create a bitarray of whether each pixel in the bounds has changed, and count
			// how many changed pixels we need to store.
			var bitmask = new BitArray (bounds.Width * bounds.Height);
			int index = 0;
			int num_changed = 0;

			int bottom = bounds.Bottom;
			int right = bounds.Right;
			int bounds_x = bounds.X;
			int bounds_y = bounds.Y;

			for (int y = bounds_y; y <= bottom; ++y) {
				var offset = y * orig_width;
				var updated = updated_ptr + offset + bounds_x;
				var orig = orig_ptr + offset + bounds_x;

				for (int x = bounds_x; x <= right; ++x) {
					bool changed = *(orig++) != *(updated++);
					bitmask[index++] = changed;
					if (changed) {
						num_changed++;
					}
				}
			}			

			var savings = 100 - (float)num_changed / (float)(orig_width * orig_height) * 100;
#if DEBUG_DIFF
			Console.WriteLine ("Compressed bitmask: {0}/{1} = {2}%", num_changed, orig_height * orig_width, 100 - savings);
#endif

			if (!force && savings < MINIMUM_SAVINGS_PERCENT) {
#if DEBUG_DIFF
				Console.WriteLine ("Savings too small, returning null");
#endif
				return null;
			}

			// Store the old pixels.
			var pixels = new ColorBgra[num_changed];
			var new_ptr = (ColorBgra*)original.GetRowAddress (0);
			int mask_index = 0;

			fixed (ColorBgra* fixed_ptr = pixels) {
				var pixel_ptr = fixed_ptr;

				for (int y = bounds_y; y <= bottom; ++y) {
					var new_pixel_ptr = new_ptr + bounds_x + y * orig_width;

					for (int x = bounds_x; x <= right; ++x) {
						if (bitmask[mask_index++]) {
							*pixel_ptr++ = *new_pixel_ptr;
						}

						new_pixel_ptr++;
					}
				}
			}

#if DEBUG_DIFF
			timer.Stop();
			System.Console.WriteLine("SurfaceDiff time: " + timer.ElapsedMilliseconds);
#endif

			return new SurfaceDiff (bitmask, bounds, pixels);
		}
        public static unsafe SurfaceDiff Create(ISurface original, ISurface updated_surf, bool force = false)
        {
            if (original.Width != updated_surf.Width || original.Height != updated_surf.Height)
            {
                // If the surface changed size, only throw an error if the user forced the use of a diff.
                if (force)
                {
                    throw new InvalidOperationException("SurfaceDiff requires surfaces to be same size.");
                }
                else
                {
                    return(null);
                }
            }

            // Cache some pinvokes
            var orig_width  = original.Width;
            var orig_height = original.Height;

#if DEBUG_DIFF
            Console.WriteLine("Original surface size: {0}x{1}", orig_width, orig_height);
            System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch();
            timer.Start();
#endif

            // STEP 1 - Find the bounds of the changed pixels.
            var orig_ptr    = (int *)original.GetRowAddress(0);
            var updated_ptr = (int *)updated_surf.GetRowAddress(0);

            DiffBounds diff_bounds      = new DiffBounds(orig_width, orig_height);
            object     diff_bounds_lock = new Object();

            // Split up the work among several threads, each of which processes one row at a time
            // and updates the bounds of the changed pixels it has seen so far. At the end, the
            // results from each thread are merged together to find the overall bounds of the changed
            // pixels.
            Parallel.For <DiffBounds>(0, orig_height, () => new DiffBounds(orig_width, orig_height),
                                      (row, loop, my_bounds) => {
                var offset         = row * orig_width;
                var orig           = orig_ptr + offset;
                var updated        = updated_ptr + offset;
                bool change_in_row = false;

                for (int i = 0; i < orig_width; ++i)
                {
                    if (*(orig++) != *(updated++))
                    {
                        change_in_row   = true;
                        my_bounds.left  = System.Math.Min(my_bounds.left, i);
                        my_bounds.right = System.Math.Max(my_bounds.right, i);
                    }
                }

                if (change_in_row)
                {
                    my_bounds.top    = System.Math.Min(my_bounds.top, row);
                    my_bounds.bottom = System.Math.Max(my_bounds.bottom, row);
                }

                return(my_bounds);
            }, (my_bounds) => {
                lock (diff_bounds_lock) {
                    diff_bounds.Merge(my_bounds);
                }
                return;
            });

            var bounds = new Rectangle(diff_bounds.left, diff_bounds.top,
                                       diff_bounds.right - diff_bounds.left + 1,
                                       diff_bounds.bottom - diff_bounds.top + 1);

#if DEBUG_DIFF
            Console.WriteLine("Truncated surface size: {0}x{1}", bounds.Width, bounds.Height);
#endif

            // STEP 2 - Create a bitarray of whether each pixel in the bounds has changed, and count
            // how many changed pixels we need to store.
            var bitmask     = new BitArray(bounds.Width * bounds.Height);
            int index       = 0;
            int num_changed = 0;

            int bottom   = bounds.Bottom;
            int right    = bounds.Right;
            int bounds_x = bounds.X;
            int bounds_y = bounds.Y;

            for (int y = bounds_y; y <= bottom; ++y)
            {
                var offset  = y * orig_width;
                var updated = updated_ptr + offset + bounds_x;
                var orig    = orig_ptr + offset + bounds_x;

                for (int x = bounds_x; x <= right; ++x)
                {
                    bool changed = *(orig++) != *(updated++);
                    bitmask[index++] = changed;
                    if (changed)
                    {
                        num_changed++;
                    }
                }
            }

            var savings = 100 - (float)num_changed / (float)(orig_width * orig_height) * 100;
#if DEBUG_DIFF
            Console.WriteLine("Compressed bitmask: {0}/{1} = {2}%", num_changed, orig_height * orig_width, 100 - savings);
#endif

            if (!force && savings < MINIMUM_SAVINGS_PERCENT)
            {
#if DEBUG_DIFF
                Console.WriteLine("Savings too small, returning null");
#endif
                return(null);
            }

            // Store the old pixels.
            var pixels     = new ColorBgra[num_changed];
            var new_ptr    = (ColorBgra *)original.GetRowAddress(0);
            int mask_index = 0;

            fixed(ColorBgra *fixed_ptr = pixels)
            {
                var pixel_ptr = fixed_ptr;

                for (int y = bounds_y; y <= bottom; ++y)
                {
                    var new_pixel_ptr = new_ptr + bounds_x + y * orig_width;

                    for (int x = bounds_x; x <= right; ++x)
                    {
                        if (bitmask[mask_index++])
                        {
                            *pixel_ptr++ = *new_pixel_ptr;
                        }

                        new_pixel_ptr++;
                    }
                }
            }

#if DEBUG_DIFF
            timer.Stop();
            System.Console.WriteLine("SurfaceDiff time: " + timer.ElapsedMilliseconds);
#endif

            return(new SurfaceDiff(bitmask, bounds, pixels));
        }