using System;
using System.Drawing;
using System.Linq;
using JD.Threading;
using TeaBox;
using TeaBox.Utilities;
namespace JD.Application.View.Utilities
{
///
/// Paints in a separate thread and notifies when the costly work is done.
///
/// Parameter for the given paint request.
///
/// Two main system here:
/// 1) Threading : The requesting thread ask for the image corresponding to its paint request. The current image is returned even if it doesnt fit.
/// This first image is used until the new image is completely done. Then, when done, the thread notifies the user control which repaints using
/// this new image.
///
/// 2) QualityLevel : GDI+ locks to render images so the background thread is only useful for computation.
/// To keep a good responsivenes, two paint are done. The first one is in low quality/fast so the lock does not affect the process too much.
/// When the user stop requesting for new paint request, a last painting is done in high quality.
/// This is achived with a timer, we reset it each time a new request appear. When the time expires, we ask for the final painting.
///
public abstract class AsyncPaintCache : IDisposable
where TRequest : struct // Because of comparison facilities
{
private Image _image;
private TRequest? _currentRequest;
private readonly WakeUpThread _paintThread;
// Quality level variables
private readonly Moderator _highQualityTrigger;
private bool _lowQuality;
public AsyncPaintCache()
{
_paintThread = new WakeUpThread(DoPaint);
_highQualityTrigger = new Moderator();
_highQualityTrigger.Execute += _highQualityTrigger_Execute;
_highQualityTrigger.InitialDelay.Interval = TimeSpan.FromMilliseconds(250);
}
protected bool LowQuality
{
get { return _lowQuality; }
}
///
/// Ask for the image corresponding to the paint request.
///
public Image GetImage(TRequest request)
{
if (_currentRequest == null || !_currentRequest.Equals(request))
{
_currentRequest = request;
_lowQuality = true;
_paintThread.WakeUp();
}
return _image;
}
public void Invalidate()
{
_currentRequest = null;
}
private void DoPaint()
{
var request = _currentRequest;
if (request == null)
return;
_image = DoPaint(request.Value);
Refreshed?.Invoke(this, EventArgs.Empty);
if (LowQuality)
{
_highQualityTrigger.InitialDelay.Reset();
_highQualityTrigger.TriggerAction();
}
}
void _highQualityTrigger_Execute()
{
_lowQuality = false;
_paintThread.WakeUp();
}
protected abstract Image DoPaint(TRequest request);
///
/// Notifies the parent control that a new image is ready.
///
public event EventHandler Refreshed;
public void Dispose()
{
_image?.Dispose();
_highQualityTrigger?.Dispose();
}
}
}