using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.IO; using StreamLibrary.src; namespace StreamLibrary.UnsafeCodecs; public class UnsafeStreamCodec : IUnsafeCodec { private byte[] EncodeBuffer; private Bitmap decodedBitmap; private PixelFormat EncodedFormat; private int EncodedWidth; private int EncodedHeight; private bool UseJPEG; public override ulong CachedSize { get; internal set; } public override int BufferCount => 1; public override CodecOption CodecOptions => CodecOption.RequireSameSize; public Size CheckBlock { get; private set; } public override event IVideoCodec.VideoDebugScanningDelegate onCodeDebugScan; public override event IVideoCodec.VideoDebugScanningDelegate onDecodeDebugScan; public UnsafeStreamCodec(int ImageQuality = 100, bool UseJPEG = true) : base(ImageQuality) { CheckBlock = new Size(50, 1); this.UseJPEG = UseJPEG; } public unsafe override void CodeImage(IntPtr Scan0, Rectangle ScanArea, Size ImageSize, PixelFormat Format, Stream outStream) { lock (base.ImageProcessLock) { byte* ptr = (byte*)Scan0.ToInt32(); if (!outStream.CanWrite) { throw new Exception("Must have access to Write in the Stream"); } int num = 0; int num2 = 0; int num3 = 0; switch (Format) { case PixelFormat.Format24bppRgb: case PixelFormat.Format32bppRgb: num3 = 3; break; case PixelFormat.Format32bppPArgb: case PixelFormat.Format32bppArgb: num3 = 4; break; default: throw new NotSupportedException(Format.ToString()); } num = ImageSize.Width * num3; num2 = num * ImageSize.Height; if (EncodeBuffer == null) { EncodedFormat = Format; EncodedWidth = ImageSize.Width; EncodedHeight = ImageSize.Height; EncodeBuffer = new byte[num2]; fixed (byte* value = EncodeBuffer) { byte[] array = null; using (Bitmap bmp = new Bitmap(ImageSize.Width, ImageSize.Height, num, Format, Scan0)) { array = jpgCompression.Compress(bmp); } outStream.Write(BitConverter.GetBytes(array.Length), 0, 4); outStream.Write(array, 0, array.Length); NativeMethods.memcpy(new IntPtr(value), Scan0, (uint)num2); } return; } long position = outStream.Position; outStream.Write(new byte[4], 0, 4); int num4 = 0; if (EncodedFormat != Format) { throw new Exception("PixelFormat is not equal to previous Bitmap"); } if (EncodedWidth != ImageSize.Width || EncodedHeight != ImageSize.Height) { throw new Exception("Bitmap width/height are not equal to previous bitmap"); } List list = new List(); int num5 = 0; Size size = new Size(ScanArea.Width, CheckBlock.Height); Size size2 = new Size(ScanArea.Width % CheckBlock.Width, ScanArea.Height % CheckBlock.Height); int num6 = ScanArea.Height - size2.Height; int num7 = ScanArea.Width - size2.Width; Rectangle rectangle = default(Rectangle); List list2 = new List(); size = new Size(ScanArea.Width, size.Height); fixed (byte* ptr2 = EncodeBuffer) { for (int i = ScanArea.Y; i != ScanArea.Height; i += size.Height) { if (i == num6) { size = new Size(ScanArea.Width, size2.Height); } rectangle = new Rectangle(ScanArea.X, i, ScanArea.Width, size.Height); if (onCodeDebugScan != null) { onCodeDebugScan(rectangle); } int num8 = i * num + ScanArea.X * num3; if (NativeMethods.memcmp(ptr2 + num8, ptr + num8, (uint)num) != 0) { num5 = list.Count - 1; if (list.Count != 0 && list[num5].Y + list[num5].Height == rectangle.Y) { rectangle = (list[num5] = new Rectangle(list[num5].X, list[num5].Y, list[num5].Width, list[num5].Height + rectangle.Height)); } else { list.Add(rectangle); } } } int j = 0; int x = ScanArea.X; for (; j < list.Count; j++) { size = new Size(CheckBlock.Width, list[j].Height); for (x = ScanArea.X; x != ScanArea.Width; x += size.Width) { if (x == num7) { size = new Size(size2.Width, list[j].Height); } rectangle = new Rectangle(x, list[j].Y, size.Width, list[j].Height); bool flag = false; int count = num3 * rectangle.Width; for (int k = 0; k < rectangle.Height; k++) { int num9 = num * (rectangle.Y + k) + num3 * rectangle.X; if (NativeMethods.memcmp(ptr2 + num9, ptr + num9, (uint)count) != 0) { flag = true; } NativeMethods.memcpy(ptr2 + num9, ptr + num9, (uint)count); } if (onCodeDebugScan != null) { onCodeDebugScan(rectangle); } if (flag) { num5 = list2.Count - 1; if (list2.Count > 0 && list2[num5].X + list2[num5].Width == rectangle.X) { Rectangle rectangle3 = list2[num5]; int width = rectangle.Width + rectangle3.Width; rectangle = (list2[num5] = new Rectangle(rectangle3.X, rectangle3.Y, width, rectangle3.Height)); } else { list2.Add(rectangle); } } } } } for (int l = 0; l < list2.Count; l++) { Rectangle rectangle5 = list2[l]; int num10 = num3 * rectangle5.Width; Bitmap bitmap = new Bitmap(rectangle5.Width, rectangle5.Height, Format); BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat); int m = 0; int num11 = 0; for (; m < rectangle5.Height; m++) { int num12 = num * (rectangle5.Y + m) + num3 * rectangle5.X; NativeMethods.memcpy((byte*)bitmapData.Scan0.ToPointer() + num11, ptr + num12, (uint)num10); num11 += num10; } bitmap.UnlockBits(bitmapData); outStream.Write(BitConverter.GetBytes(rectangle5.X), 0, 4); outStream.Write(BitConverter.GetBytes(rectangle5.Y), 0, 4); outStream.Write(BitConverter.GetBytes(rectangle5.Width), 0, 4); outStream.Write(BitConverter.GetBytes(rectangle5.Height), 0, 4); outStream.Write(new byte[4], 0, 4); long length = outStream.Length; long position2 = outStream.Position; if (UseJPEG) { jpgCompression.Compress(bitmap, ref outStream); } else { lzwCompression.Compress(bitmap, outStream); } length = outStream.Position - length; outStream.Position = position2 - 4; outStream.Write(BitConverter.GetBytes((int)length), 0, 4); outStream.Position += length; bitmap.Dispose(); num4 += (int)length + 20; } outStream.Position = position; outStream.Write(BitConverter.GetBytes(num4), 0, 4); list.Clear(); list2.Clear(); } } public unsafe override Bitmap DecodeData(IntPtr CodecBuffer, uint Length) { if (Length < 4) { return decodedBitmap; } int num = *(int*)(void*)CodecBuffer; if (decodedBitmap == null) { byte[] array = new byte[num]; fixed (byte* value = array) { NativeMethods.memcpy(new IntPtr(value), new IntPtr(CodecBuffer.ToInt32() + 4), (uint)num); } decodedBitmap = (Bitmap)Image.FromStream(new MemoryStream(array)); return decodedBitmap; } return decodedBitmap; } public override Bitmap DecodeData(Stream inStream) { try { byte[] array = new byte[4]; inStream.Read(array, 0, 4); int num = BitConverter.ToInt32(array, 0); if (decodedBitmap == null) { array = new byte[num]; inStream.Read(array, 0, array.Length); decodedBitmap = (Bitmap)Image.FromStream(new MemoryStream(array)); return decodedBitmap; } using (Graphics graphics = Graphics.FromImage(decodedBitmap)) { while (num > 0) { byte[] array2 = new byte[20]; inStream.Read(array2, 0, array2.Length); Rectangle scanArea = new Rectangle(BitConverter.ToInt32(array2, 0), BitConverter.ToInt32(array2, 4), BitConverter.ToInt32(array2, 8), BitConverter.ToInt32(array2, 12)); int num2 = BitConverter.ToInt32(array2, 16); array2 = null; byte[] array3 = new byte[num2]; inStream.Read(array3, 0, array3.Length); if (onDecodeDebugScan != null) { onDecodeDebugScan(scanArea); } using (MemoryStream stream = new MemoryStream(array3)) { using Bitmap image = (Bitmap)Image.FromStream(stream); graphics.DrawImage(image, scanArea.Location); } array3 = null; num -= num2 + 20; } } return decodedBitmap; } catch { return null; } } }