IPCamAppBar/IPCamAppBar/CameraView.cs

178 lines
5.4 KiB
C#

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using FastBitmapLib;
using IPCamLib;
namespace IPCamAppBar
{
public partial class CameraView : UserControl, IRetryableCameraObserver
{
private readonly ICamera camera;
private readonly bool overlayDateTime;
private Bitmap viewBitmap;
private CancellationTokenSource streamCancellationTokenSource;
public CameraView(ICamera camera, bool overlayDateTime)
{
this.camera = camera;
this.overlayDateTime = overlayDateTime;
InitializeComponent();
StartCamera();
Disposed += (_, _) =>
{
StopCamera();
};
}
private void StartCamera()
{
streamCancellationTokenSource = new CancellationTokenSource();
Task.Run(async () =>
{
var retryableCamera = new RetryableCamera(camera);
await retryableCamera.Fetch(this, streamCancellationTokenSource.Token);
});
}
private void StopCamera()
{
streamCancellationTokenSource.Cancel();
}
private Bitmap resizedBitmap;
public Task OnFrame(Image image)
{
if (streamCancellationTokenSource.IsCancellationRequested)
return Task.CompletedTask;
if (overlayDateTime || image is not Bitmap bitmap || image.Width != Width || image.Height != Height)
{
resizedBitmap ??= new Bitmap(Width, Height);
using var graphics = Graphics.FromImage(resizedBitmap);
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.DrawImage(image, 0, 0, resizedBitmap.Width, resizedBitmap.Height);
if (overlayDateTime)
{
using (var path = new GraphicsPath())
{
path.AddString(
DateTime.Now.ToString("G"),
FontFamily.GenericSansSerif,
(int) FontStyle.Regular,
graphics.DpiY * 14 / 72,
Rectangle.Inflate(new Rectangle(0, 0, resizedBitmap.Width, resizedBitmap.Height), -4, -4),
new StringFormat
{
Alignment = StringAlignment.Far,
LineAlignment = StringAlignment.Far
});
graphics.DrawPath(new Pen(Color.Black, 3), path);
graphics.FillPath(Brushes.White, path);
}
}
graphics.Flush();
bitmap = resizedBitmap;
}
Invoke(new Action(() => { InvokeFrame(bitmap); }));
return Task.CompletedTask;
}
public Task OnFetch()
{
Invoke(new Action(InvokeFetch));
return Task.CompletedTask;
}
public Task OnDisconnected(Exception exception, TimeSpan retryDelay)
{
Invoke(new Action(() => { InvokeDisconnected(exception, retryDelay); }));
return Task.CompletedTask;
}
private void InvokeFrame(Bitmap bitmap)
{
if (IsDisposed)
return;
ConnectingLabel.Visible = false;
StreamView.Visible = true;
IssueLabel.Visible = false;
PausedOverlay.Visible = false;
if (viewBitmap == null)
{
viewBitmap = new Bitmap(Width, Height);
StreamView.Image = viewBitmap;
}
using (var fastViewBitmap = viewBitmap.FastLock())
{
fastViewBitmap.CopyRegion(bitmap, new Rectangle(Point.Empty, bitmap.Size), new Rectangle(Point.Empty, viewBitmap.Size));
}
StreamView.Invalidate();
}
private void InvokeFetch()
{
if (IsDisposed)
return;
IssueLabel.Text = "Connecting...";
IssueLabel.Visible = true;
IssueLabel.BringToFront();
}
private void InvokeDisconnected(Exception exception, TimeSpan retryDelay)
{
if (IsDisposed || streamCancellationTokenSource.IsCancellationRequested)
return;
IssueLabel.Text = (exception?.Message ?? "Camera disconnected") + $", retrying in {retryDelay.TotalSeconds} seconds";
IssueLabel.Visible = true;
IssueLabel.BringToFront();
}
public void SetPaused(bool pause)
{
if (pause)
{
StopCamera();
PausedOverlay.Left = (Width - PausedOverlay.Width) / 2;
PausedOverlay.Top = (Height - PausedOverlay.Height) / 2;
PausedOverlay.Visible = true;
PausedOverlay.BringToFront();
}
else
StartCamera();
}
}
}