using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Threading;
using LensAdjustmentTool.Configuration;
using LensAdjustmentTool.Models;
using TIS.Imaging;
namespace LensAdjustmentTool.Processing
{
public sealed class LensAdjustmentProcessor
{
private int _mode = (int)AdjustmentMode.Focus;
private readonly object _focusMaxSync = new object();
private double _focusMaximum;
public AdjustmentMode Mode => (AdjustmentMode)Volatile.Read(ref _mode);
public void SetMode(AdjustmentMode mode, bool resetFocusMaximum)
{
Interlocked.Exchange(ref _mode, (int)mode);
if (mode == AdjustmentMode.Focus && resetFocusMaximum)
{
ResetFocusMaximum();
}
}
public void ResetFocusMaximum()
{
lock (_focusMaxSync)
{
_focusMaximum = 0.0;
}
}
public FrameAnalysisResult Process(IFrameQueueBuffer frame)
{
if (frame == null) throw new ArgumentNullException(nameof(frame));
AdjustmentMode mode = Mode;
using (Bitmap wrapped = frame.CreateBitmapWrap())
{
if (wrapped.Width != AppSettings.ExpectedFrameWidth ||
wrapped.Height != AppSettings.ExpectedFrameHeight)
{
throw new InvalidOperationException(
$"Unexpected frame size: {wrapped.Width}x{wrapped.Height}. " +
$"Expected {AppSettings.ExpectedFrameWidth}x{AppSettings.ExpectedFrameHeight}.");
}
return mode == AdjustmentMode.Focus
? ProcessFocus(wrapped)
: ProcessAperture(wrapped);
}
}
private FrameAnalysisResult ProcessFocus(Bitmap fullFrame)
{
Bitmap roi = Clone24(fullFrame, AppSettings.FocusRoi);
try
{
double current = FocusAnalyzer.CalculateScore(roi);
double maximum;
lock (_focusMaxSync)
{
if (current > _focusMaximum)
{
_focusMaximum = current;
}
maximum = _focusMaximum;
}
// System.Drawing Rotate90FlipNone is a 90-degree clockwise rotation.
roi.RotateFlip(RotateFlipType.Rotate90FlipNone);
Bitmap display = roi;
roi = null;
return new FrameAnalysisResult(
AdjustmentMode.Focus,
display,
current,
maximum);
}
finally
{
roi?.Dispose();
}
}
private FrameAnalysisResult ProcessAperture(Bitmap fullFrame)
{
Bitmap display = Clone24(fullFrame, AppSettings.ApertureDisplayRoi);
try
{
Rectangle relativeCalc = AppSettings.ApertureCalculationRoiRelativeToDisplay;
using (Bitmap calculation = Clone24(display, relativeCalc))
{
double mean = ApertureAnalyzer.CalculateMeanLuminance(calculation);
using (Graphics g = Graphics.FromImage(display))
using (Pen pen = new Pen(Color.Red, 3.0f))
{
g.DrawRectangle(
pen,
relativeCalc.X,
relativeCalc.Y,
relativeCalc.Width - 1,
relativeCalc.Height - 1);
}
Bitmap ownedDisplay = display;
display = null;
return new FrameAnalysisResult(
AdjustmentMode.Aperture,
ownedDisplay,
mean,
mean - AppSettings.ApertureTarget);
}
}
finally
{
display?.Dispose();
}
}
private static Bitmap Clone24(Bitmap source, Rectangle roi)
{
Rectangle bounds = new Rectangle(0, 0, source.Width, source.Height);
if (!bounds.Contains(roi))
{
throw new ArgumentOutOfRangeException(
nameof(roi),
$"ROI {roi} is outside image bounds {bounds}.");
}
return source.Clone(roi, PixelFormat.Format24bppRgb);
}
}
}