private unsafe List <Rectangle> ProcessChanges(IntPtr Scan0, Rectangle OutputRect, PixelFormat Format, int ImageWidth) { if (EncodeBuffer == null) { this.BlockWidth = (int)Math.Floor((float)(OutputRect.Width / CheckBlock.Width)); this.BlockHeight = (int)Math.Floor((double)(OutputRect.Height / CheckBlock.Height)); int TotalBlocks = (int)Math.Floor((float)(BlockHeight * BlockWidth)); this.EncodeBuffer = new ulong[TotalBlocks]; } List <Rectangle> points = new List <Rectangle>(); int StartScan = Scan0.ToInt32(); for (int y = OutputRect.Y; y < OutputRect.Height + OutputRect.Y; y += CheckBlock.Height) { if (y + CheckBlock.Height > OutputRect.Height) { break; } for (int x = OutputRect.X; x < OutputRect.Width + OutputRect.X; x += CheckBlock.Width) { if (x + CheckBlock.Width > OutputRect.Width) { break; } int EncodeOffset = GetOffset(x, y); long offset = FastBitmap.CalcImageOffset(x, y, Format, ImageWidth); ulong *ScanPtr = (ulong *)(StartScan + offset); if (EncodeBuffer[EncodeOffset] != *ScanPtr) { EncodeBuffer[EncodeOffset] = *ScanPtr; Rectangle cBlock = new Rectangle(x, y, CheckBlock.Width, CheckBlock.Height); int index = points.Count - 1; if (points.Count > 0 && (points[index].X + points[index].Width) == cBlock.X) { Rectangle rect = points[index]; int newWidth = cBlock.Width + rect.Width; cBlock = new Rectangle(rect.X, rect.Y, newWidth, rect.Height); points[index] = cBlock; } else { points.Add(cBlock); } } } } return(points); }
public override unsafe void CodeImage(IntPtr Scan0, Rectangle ScanArea, Size ImageSize, PixelFormat Format, Stream outStream) { byte *pScan0 = (byte *)Scan0.ToInt32(); if (!outStream.CanWrite) { throw new Exception("Must have access to Write in the Stream"); } int Stride = 0; int RawLength = 0; int PixelSize = 0; switch (Format) { case PixelFormat.Format24bppRgb: PixelSize = 3; break; case PixelFormat.Format32bppArgb: case PixelFormat.Format32bppPArgb: PixelSize = 4; break; default: throw new NotSupportedException(Format.ToString()); } Stride = ImageSize.Width * PixelSize; RawLength = Stride * ImageSize.Height; if (EncodedWidth == 0 && EncodedHeight == 0) { this.EncodedFormat = Format; this.EncodedWidth = ImageSize.Width; this.EncodedHeight = ImageSize.Height; byte[] temp = null; using (Bitmap TmpBmp = new Bitmap(ImageSize.Width, ImageSize.Height, Stride, Format, Scan0)) { temp = base.jpgCompression.Compress(TmpBmp); } outStream.Write(BitConverter.GetBytes(temp.Length), 0, 4); outStream.Write(temp, 0, temp.Length); return; } Frame frame = new Frame(ImageSize.Width, ImageSize.Height); int Blocks = (ScanArea.Width / CheckBlock.Width) * (ScanArea.Height / CheckBlock.Height); int RawSizeUsed = 0; long oldPos = outStream.Position; outStream.Write(new byte[4], 0, 4); int TotalDataLength = 0; List <Rectangle> ChangedBlocks = new List <Rectangle>(); for (int y = ScanArea.Y; y < ScanArea.Height;) { int height = y + CheckBlock.Height < ScanArea.Height ? CheckBlock.Height : ScanArea.Height - y; for (int x = ScanArea.X; x < ScanArea.Width;) { int width = x + CheckBlock.Width < ScanArea.Width ? CheckBlock.Width : ScanArea.Width - x; int BlockStride = Format == PixelFormat.Format24bppRgb ? width * 3 : width * 4; decimal FinalHash = 0; for (int h = 0; h < height; h++) { int Offset = FastBitmap.CalcImageOffset(x, y + h, Format, ImageSize.Width); FinalHash += Hasher.Hash(pScan0 + Offset, BlockStride); } if (onCodeDebugScan != null) { onCodeDebugScan(new Rectangle(x, y, width, height)); } bool FoundBlock = false; decimal FoundHash = 0; int FrameIndex = 0; for (int i = 0; i < Frames.Count; i++) { decimal hash = Frames[i].GetHashBlock(x, y); if (hash == FinalHash) { FrameIndex = i; FoundBlock = true; FoundHash = hash; break; } } frame.AddHashBlock(x, y, FinalHash); if (!FoundBlock) { int index = ChangedBlocks.Count - 1; Rectangle cBlock = new Rectangle(x, y, width, height); if (ChangedBlocks.Count > 0 && (ChangedBlocks[index].X + ChangedBlocks[index].Width) == cBlock.X) { Rectangle rect = ChangedBlocks[index]; int newWidth = cBlock.Width + rect.Width; cBlock = new Rectangle(rect.X, rect.Y, newWidth, rect.Height); ChangedBlocks[index] = cBlock; } /*else if (ChangedBlocks.Count > 0 && (ChangedBlocks[index].Y + ChangedBlocks[index].Height) == cBlock.Y) * { * Rectangle rect = ChangedBlocks[index]; * int newHeight = cBlock.Height + rect.Height; * cBlock = new Rectangle(rect.X, rect.Y, rect.Width, newHeight); * ChangedBlocks[index] = cBlock; * }*/ else { ChangedBlocks.Add(cBlock); } RawSizeUsed += BlockStride * height; } x += width; } y += height; } //write all the blocks for (int i = 0; i < ChangedBlocks.Count; i++) { Rectangle rect = ChangedBlocks[i]; int blockStride = PixelSize * rect.Width; Bitmap TmpBmp = new Bitmap(rect.Width, rect.Height, Format); BitmapData TmpData = TmpBmp.LockBits(new Rectangle(0, 0, TmpBmp.Width, TmpBmp.Height), ImageLockMode.ReadWrite, TmpBmp.PixelFormat); for (int j = 0, offset = 0; j < rect.Height; j++) { int blockOffset = (Stride * (rect.Y + j)) + (PixelSize * rect.X); NativeMethods.memcpy((byte *)TmpData.Scan0.ToPointer() + offset, pScan0 + blockOffset, (uint)blockStride); //copy-changes offset += blockStride; } TmpBmp.UnlockBits(TmpData); outStream.Write(BitConverter.GetBytes(rect.X), 0, 4); outStream.Write(BitConverter.GetBytes(rect.Y), 0, 4); outStream.Write(BitConverter.GetBytes(rect.Width), 0, 4); outStream.Write(BitConverter.GetBytes(rect.Height), 0, 4); outStream.Write(new byte[4], 0, 4); long length = outStream.Position; long OldPos = outStream.Position; base.jpgCompression.Compress(TmpBmp, ref outStream); length = outStream.Position - length; outStream.Position = OldPos - 4; outStream.Write(BitConverter.GetBytes((int)length), 0, 4); outStream.Position += length; TmpBmp.Dispose(); TotalDataLength += (int)length + (4 * 5); } outStream.Position = oldPos; outStream.Write(BitConverter.GetBytes(TotalDataLength), 0, 4); ChangedBlocks.Clear(); Frames.Add(frame); if (Frames.Count > MAX_FRAMES) { Frames.RemoveAt(0); } }
public override unsafe void CodeImage(IntPtr Scan0, Rectangle ScanArea, Size ImageSize, PixelFormat Format, Stream outStream) { lock (this.ImageProcessLock) { byte *pScan0 = (byte *)Scan0.ToInt32(); if (!outStream.CanWrite) { throw new Exception("Must have access to Write in the Stream"); } int Stride = 0; int RawLength = 0; int PixelSize = 0; FastBitmap.CalcImageOffset(0, 0, Format, ScanArea.Width); //check for FastBitmap Support switch (Format) { case PixelFormat.Format24bppRgb: case PixelFormat.Format32bppRgb: PixelSize = 3; break; case PixelFormat.Format32bppArgb: case PixelFormat.Format32bppPArgb: PixelSize = 4; break; default: throw new NotSupportedException(Format + " is not supported."); } Stride = ImageSize.Width * PixelSize; RawLength = Stride * ImageSize.Height; //first frame if (EncodeBuffer == null) { this.EncodedFormat = Format; this.EncodedWidth = ImageSize.Width; this.EncodedHeight = ImageSize.Height; this.EncodeBuffer = new byte[RawLength]; fixed(byte *ptr = EncodeBuffer) { byte[] temp = null; using (Bitmap TmpBmp = new Bitmap(ImageSize.Width, ImageSize.Height, Stride, Format, Scan0)) { temp = base.jpgCompression.Compress(TmpBmp); } outStream.Write(BitConverter.GetBytes(temp.Length), 0, 4); outStream.Write(temp, 0, temp.Length); NativeMethods.memcpy(new IntPtr(ptr), Scan0, (uint)RawLength); } return; } if (this.EncodedFormat != Format) { throw new Exception("PixelFormat is not equal to previous Bitmap"); } if (this.EncodedWidth != ImageSize.Width || this.EncodedHeight != ImageSize.Height) { throw new Exception("Bitmap width/height are not equal to previous bitmap"); } if (ScanArea.Width > ImageSize.Width || ImageSize.Height > this.EncodedHeight) { throw new Exception("Scan Area Width/Height cannot be greater then the encoded image"); } List <Rectangle> Blocks = new List <Rectangle>(); //all the changes fixed(byte *encBuffer = EncodeBuffer) { //1. Check for the changes in height for (int y = ScanArea.Y; y < ScanArea.Height; y++) { Rectangle cBlock = new Rectangle(0, y, ImageSize.Width, 1); if (onCodeDebugScan != null) { onCodeDebugScan(cBlock); } int Offset = FastBitmap.CalcImageOffset(0, y, Format, ImageSize.Width); if (NativeMethods.memcmp(encBuffer + Offset, pScan0 + Offset, (uint)Stride) != 0) { int index = Blocks.Count - 1; 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 { Blocks.Add(cBlock); } } } //2. Capture all the changes using the CheckBlock List <Rectangle> finalUpdates = new List <Rectangle>(); for (int i = 0; i < Blocks.Count; i++) { Rectangle scanBlock = Blocks[i]; //go through the Blocks for (int y = scanBlock.Y; y < scanBlock.Height; y += CheckBlock.Height) { for (int x = scanBlock.X; x < scanBlock.Width; x += CheckBlock.Width) { int blockWidth = x + CheckBlock.Width < scanBlock.Width ? CheckBlock.Width : scanBlock.Width - x; int blockHeight = y + CheckBlock.Height < scanBlock.Height ? CheckBlock.Height : scanBlock.Height - y; Rectangle cBlock = new Rectangle(x, y, blockWidth, blockHeight); if (onCodeDebugScan != null) { onCodeDebugScan(cBlock); } //scan the block from Top To Bottom and check for changes bool FoundChanges = false; for (int blockY = y; blockY < y + blockHeight; blockY++) { int Offset = FastBitmap.CalcImageOffset(x, blockY, Format, blockWidth); if (NativeMethods.memcmp(encBuffer + Offset, pScan0 + Offset, (uint)Stride) != 0) { FoundChanges = true; break; } } if (FoundChanges) { int index = finalUpdates.Count - 1; if (finalUpdates.Count > 0 && (finalUpdates[index].X + finalUpdates[index].Width) == cBlock.X) { Rectangle rect = finalUpdates[index]; int newWidth = cBlock.Width + rect.Width; cBlock = new Rectangle(rect.X, rect.Y, newWidth, rect.Height); finalUpdates[index] = cBlock; } else { finalUpdates.Add(cBlock); } } } } } //maybe a too hard algorithm but oh well SortedList <int, SortedList <int, Rectangle> > Array = finalUpdates.ToArray().RectanglesTo2D().Rectangle2DToRows(); List <Rectangle> FinalTemp = new List <Rectangle>(); for (int i = 0; i < Array.Values.Count; i++) { FinalTemp.AddRange(Array.Values[i].Values); } //fixup the height for (int i = 0; i < FinalTemp.Count;) { if (FinalTemp.Count == 1) { FinalTemp.Add(FinalTemp[i]); break; } if (i + 1 < FinalTemp.Count) { Rectangle curRect = FinalTemp[i]; Rectangle nextRect = FinalTemp[i + 1]; if ((curRect.Y + curRect.Height) == nextRect.Y && curRect.Width == nextRect.Width) { FinalTemp[i] = new Rectangle(curRect.X, curRect.Y, curRect.Width, curRect.Height + curRect.Height); FinalTemp.RemoveAt(i + 1); } else { i++; } } else { break; } } //copy changes to the EncodeBuffer and Process the Output long oldPos = outStream.Position; outStream.Write(new byte[4], 0, 4); int TotalDataLength = 0; for (int i = 0; i < FinalTemp.Count; i++) { Rectangle rect = FinalTemp[i]; int blockStride = PixelSize * rect.Width; //copy changes to EncodeBuffer for (int y = rect.Y; y < rect.Y + rect.Height; y++) { int Offset = FastBitmap.CalcImageOffset(rect.X, y, Format, rect.Width); NativeMethods.memcpy(encBuffer + Offset, pScan0 + Offset, (uint)blockStride); //copy-changes } Bitmap TmpBmp = new Bitmap(rect.Width, rect.Height, Format); BitmapData TmpData = TmpBmp.LockBits(new Rectangle(0, 0, TmpBmp.Width, TmpBmp.Height), ImageLockMode.ReadWrite, TmpBmp.PixelFormat); for (int j = 0, offset = 0; j < rect.Height; j++) { int blockOffset = (Stride * (rect.Y + j)) + (PixelSize * rect.X); NativeMethods.memcpy((byte *)TmpData.Scan0.ToPointer() + offset, pScan0 + blockOffset, (uint)blockStride); //copy-changes offset += blockStride; } TmpBmp.UnlockBits(TmpData); outStream.Write(BitConverter.GetBytes(rect.X), 0, 4); outStream.Write(BitConverter.GetBytes(rect.Y), 0, 4); outStream.Write(BitConverter.GetBytes(rect.Width), 0, 4); outStream.Write(BitConverter.GetBytes(rect.Height), 0, 4); outStream.Write(new byte[4], 0, 4); long length = outStream.Position; long OldPos = outStream.Position; base.jpgCompression.Compress(TmpBmp, ref outStream); length = outStream.Position - length; outStream.Position = OldPos - 4; outStream.Write(BitConverter.GetBytes((int)length), 0, 4); outStream.Position += length; TmpBmp.Dispose(); TotalDataLength += (int)length + (4 * 5); } outStream.Position = oldPos; outStream.Write(BitConverter.GetBytes(TotalDataLength), 0, 4); Blocks.Clear(); } } }