﻿// Decompiled with JetBrains decompiler
// Type: InvokedCommon.Video.Codecs.UnsafeStreamCodec
// Assembly: InvokedCommon, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: A935EE8A-7FFF-4BAB-94FD-31AEC4036FB9
// Assembly location: C:\Users\ian\Downloads\Telegram Desktop\fein\InvokedCommon.dll

using InvokedCommon.Video.Compression;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;


namespace InvokedCommon.Video.Codecs
{
	public class UnsafeStreamCodec : IDisposable
	{
		private int _imageQuality;
		private byte[] _encodeBuffer;
		private Bitmap _decodedBitmap;
		private PixelFormat _encodedFormat;
		private int _encodedWidth;
		private int _encodedHeight;
		private readonly object _imageProcessLock = new object();
		private JpgCompression _jpgCompression;

		public int Monitor { get; private set; }

		public Resolution Resolution { get; private set; }

		public Size CheckBlock { get; private set; }

		public int ImageQuality
		{
			get => this._imageQuality;
			private set
			{
				lock (this._imageProcessLock)
				{
					this._imageQuality = value;
					if (this._jpgCompression != null)
						this._jpgCompression.Dispose();
					this._jpgCompression = new JpgCompression((long)this._imageQuality);
				}
			}
		}

		public UnsafeStreamCodec(int imageQuality, int monitor, Resolution resolution)
		{
			this.ImageQuality = imageQuality;
			this.Monitor = monitor;
			this.Resolution = resolution;
			this.CheckBlock = new Size(50, 1);
		}

		public void Dispose()
		{
			this.Dispose(true);
			GC.SuppressFinalize((object)this);
		}

		protected virtual void Dispose(bool disposing)
		{
			if (!disposing)
				return;
			if (this._decodedBitmap != null)
				this._decodedBitmap.Dispose();
			if (this._jpgCompression == null)
				return;
			this._jpgCompression.Dispose();
		}

		public unsafe void CodeImage(
		    IntPtr scan0,
		    Rectangle scanArea,
		    Size imageSize,
		    PixelFormat format,
		    Stream outStream)
		{
			lock (this._imageProcessLock)
			{
				byte* numPtr1 = IntPtr.Size != 8 ? (byte*)scan0.ToInt32() : (byte*)scan0.ToInt64();
				if (!outStream.CanWrite)
					throw new Exception("Must have access to Write in the Stream");
				int num1;
				if (format <= PixelFormat.Format32bppRgb)
				{
					if (format == PixelFormat.Format24bppRgb || format == PixelFormat.Format32bppRgb)
					{
						num1 = 3;
						goto label_9;
					}
				}
				else if (format == PixelFormat.Format32bppPArgb || format == PixelFormat.Format32bppArgb)
				{
					num1 = 4;
					goto label_9;
				}
				throw new NotSupportedException(format.ToString());
			label_9:
				int num2 = imageSize.Width * num1;
				int count1 = num2 * imageSize.Height;
				if (this._encodeBuffer == null)
				{
					this._encodedFormat = format;
					this._encodedWidth = imageSize.Width;
					this._encodedHeight = imageSize.Height;
					this._encodeBuffer = new byte[count1];
					fixed (byte* numPtr2 = this._encodeBuffer)
					{
						byte[] buffer = (byte[])null;
						using (Bitmap bmp = new Bitmap(imageSize.Width, imageSize.Height, num2, format, scan0))
							buffer = this._jpgCompression.Compress(bmp);
						outStream.Write(BitConverter.GetBytes(buffer.Length), 0, 4);
						outStream.Write(buffer, 0, buffer.Length);
						NativeMethods.memcpy(new IntPtr((void*)numPtr2), scan0, (uint)count1);
					}
				}
				else
				{
					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");
					long position1 = outStream.Position;
					outStream.Write(new byte[4], 0, 4);
					long num3 = 0;
					List<Rectangle> rectangleList1 = new List<Rectangle>();
					Size size1 = new Size(scanArea.Width, this.CheckBlock.Height);
					Size size2 = default;
					ref Size local1 = ref size2;
					int width1 = scanArea.Width;
					Size checkBlock = this.CheckBlock;
					int width2 = checkBlock.Width;
					int width3 = width1 % width2;
					int height1 = scanArea.Height;
					checkBlock = this.CheckBlock;
					int height2 = checkBlock.Height;
					int height3 = height1 % height2;
					local1 = new Size(width3, height3);
					int num4 = scanArea.Height - size2.Height;
					int num5 = scanArea.Width - size2.Width;
					Rectangle rectangle1 = new Rectangle();
					List<Rectangle> rectangleList2 = new List<Rectangle>();
					size1 = new Size(scanArea.Width, size1.Height);
					fixed (byte* numPtr3 = this._encodeBuffer)
					{
						Rectangle rectangle2;
						for (int y1 = scanArea.Y; y1 != scanArea.Height; y1 += size1.Height)
						{
							if (y1 == num4)
								size1 = new Size(scanArea.Width, size2.Height);
							rectangle1 = new Rectangle(scanArea.X, y1, scanArea.Width, size1.Height);
							int num6 = y1 * num2 + scanArea.X * num1;
							if (NativeMethods.memcmp(numPtr3 + num6, numPtr1 + num6, (uint)num2) != 0)
							{
								int index = rectangleList1.Count - 1;
								if (rectangleList1.Count != 0)
								{
									rectangle2 = rectangleList1[index];
									int y2 = rectangle2.Y;
									rectangle2 = rectangleList1[index];
									int height4 = rectangle2.Height;
									if (y2 + height4 == rectangle1.Y)
									{
										ref Rectangle local2 = ref rectangle1;
										rectangle2 = rectangleList1[index];
										int x = rectangle2.X;
										rectangle2 = rectangleList1[index];
										int y3 = rectangle2.Y;
										rectangle2 = rectangleList1[index];
										int width4 = rectangle2.Width;
										rectangle2 = rectangleList1[index];
										int height5 = rectangle2.Height + rectangle1.Height;
										local2 = new Rectangle(x, y3, width4, height5);
										rectangleList1[index] = rectangle1;
										continue;
									}
								}
								rectangleList1.Add(rectangle1);
							}
						}
						for (int index1 = 0; index1 < rectangleList1.Count; ++index1)
						{
							ref Size local3 = ref size1;
							checkBlock = this.CheckBlock;
							int width5 = checkBlock.Width;
							rectangle2 = rectangleList1[index1];
							int height6 = rectangle2.Height;
							local3 = new Size(width5, height6);
							for (int x1 = scanArea.X; x1 != scanArea.Width; x1 += size1.Width)
							{
								if (x1 == num5)
								{
									ref Size local4 = ref size1;
									int width6 = size2.Width;
									rectangle2 = rectangleList1[index1];
									int height7 = rectangle2.Height;
									local4 = new Size(width6, height7);
								}
								ref Rectangle local5 = ref rectangle1;
								int x2 = x1;
								rectangle2 = rectangleList1[index1];
								int y = rectangle2.Y;
								int width7 = size1.Width;
								rectangle2 = rectangleList1[index1];
								int height8 = rectangle2.Height;
								local5 = new Rectangle(x2, y, width7, height8);
								bool flag = false;
								uint count2 = (uint)(num1 * rectangle1.Width);
								for (int index2 = 0; index2 < rectangle1.Height; ++index2)
								{
									int num7 = num2 * (rectangle1.Y + index2) + num1 * rectangle1.X;
									if (NativeMethods.memcmp(numPtr3 + num7, numPtr1 + num7, count2) != 0)
										flag = true;
									NativeMethods.memcpy((void*)(numPtr3 + num7), (void*)(numPtr1 + num7), count2);
								}
								if (flag)
								{
									int index3 = rectangleList2.Count - 1;
									if (rectangleList2.Count > 0)
									{
										rectangle2 = rectangleList2[index3];
										int x3 = rectangle2.X;
										rectangle2 = rectangleList2[index3];
										int width8 = rectangle2.Width;
										if (x3 + width8 == rectangle1.X)
										{
											Rectangle rectangle3 = rectangleList2[index3];
											int width9 = rectangle1.Width + rectangle3.Width;
											rectangle1 = new Rectangle(rectangle3.X, rectangle3.Y, width9, rectangle3.Height);
											rectangleList2[index3] = rectangle1;
											continue;
										}
									}
									rectangleList2.Add(rectangle1);
								}
							}
						}
					}
					for (int index = 0; index < rectangleList2.Count; ++index)
					{
						Rectangle rectangle4 = rectangleList2[index];
						int count3 = num1 * rectangle4.Width;
						Bitmap bmp = (Bitmap)null;
						BitmapData bitmapdata = (BitmapData)null;
						long num8;
						try
						{
							bmp = new Bitmap(rectangle4.Width, rectangle4.Height, format);
							bitmapdata = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat);
							int num9 = 0;
							int num10 = 0;
							for (; num9 < rectangle4.Height; ++num9)
							{
								int num11 = num2 * (rectangle4.Y + num9) + num1 * rectangle4.X;
								NativeMethods.memcpy((void*)((IntPtr)bitmapdata.Scan0.ToPointer() + num10), (void*)(numPtr1 + num11), (uint)count3);
								num10 += count3;
							}
							outStream.Write(BitConverter.GetBytes(rectangle4.X), 0, 4);
							outStream.Write(BitConverter.GetBytes(rectangle4.Y), 0, 4);
							outStream.Write(BitConverter.GetBytes(rectangle4.Width), 0, 4);
							outStream.Write(BitConverter.GetBytes(rectangle4.Height), 0, 4);
							outStream.Write(new byte[4], 0, 4);
							num8 = outStream.Length;
							long position2 = outStream.Position;
							this._jpgCompression.Compress(bmp, ref outStream);
							num8 = outStream.Position - num8;
							outStream.Position = position2 - 4L;
							outStream.Write(BitConverter.GetBytes(num8), 0, 4);
							outStream.Position += num8;
						}
						finally
						{
							bmp.UnlockBits(bitmapdata);
							bmp.Dispose();
						}
						num3 += num8 + 20L;
					}
					outStream.Position = position1;
					outStream.Write(BitConverter.GetBytes(num3), 0, 4);
				}
			}
		}

		public unsafe Bitmap DecodeData(IntPtr codecBuffer, uint length)
		{
			if (length < 4U)
				return this._decodedBitmap;
			int count = *(int*)(void*)codecBuffer;
			if (this._decodedBitmap != null)
				return this._decodedBitmap;
			byte[] buffer = new byte[count];
			fixed (byte* numPtr = buffer)
				NativeMethods.memcpy(new IntPtr((void*)numPtr), new IntPtr(codecBuffer.ToInt32() + 4), (uint)count);
			this._decodedBitmap = (Bitmap)Image.FromStream((Stream)new MemoryStream(buffer));
			return this._decodedBitmap;
		}

		public Bitmap DecodeData(Stream inStream)
		{
			byte[] buffer1 = new byte[4];
			inStream.Read(buffer1, 0, 4);
			int int32_1 = BitConverter.ToInt32(buffer1, 0);
			if (this._decodedBitmap == null)
			{
				byte[] buffer2 = new byte[int32_1];
				inStream.Read(buffer2, 0, buffer2.Length);
				this._decodedBitmap = (Bitmap)Image.FromStream((Stream)new MemoryStream(buffer2));
				return this._decodedBitmap;
			}
			int int32_2;
			using (Graphics graphics = Graphics.FromImage((Image)this._decodedBitmap))
			{
				for (; int32_1 > 0; int32_1 -= int32_2 + 20)
				{
					byte[] buffer3 = new byte[20];
					inStream.Read(buffer3, 0, buffer3.Length);
					Rectangle rectangle = new Rectangle(BitConverter.ToInt32(buffer3, 0), BitConverter.ToInt32(buffer3, 4), BitConverter.ToInt32(buffer3, 8), BitConverter.ToInt32(buffer3, 12));
					int32_2 = BitConverter.ToInt32(buffer3, 16);
					byte[] buffer4 = new byte[int32_2];
					inStream.Read(buffer4, 0, buffer4.Length);
					using (MemoryStream memoryStream = new MemoryStream(buffer4))
					{
						using (Bitmap bitmap = (Bitmap)Image.FromStream((Stream)memoryStream))
							graphics.DrawImage((Image)bitmap, rectangle.Location);
					}
				}
			}
			return this._decodedBitmap;
		}
	}
}