Skip to content

Instantly share code, notes, and snippets.

@rickbrew
Created August 6, 2021 16:27
Show Gist options
  • Save rickbrew/4146e7baa050a0cc09a19f1acef6f340 to your computer and use it in GitHub Desktop.
Save rickbrew/4146e7baa050a0cc09a19f1acef6f340 to your computer and use it in GitHub Desktop.
private static unsafe void FillStencilFromPoint<TBitVector2D, TCheckColorFn>(
IRenderer<ColorBgra> sampleSource,
TBitVector2D stencilBuffer,
Point2Int32 startPt,
byte tolerance,
TCheckColorFn checkColorFn,
ICancellationToken cancelToken,
out RectInt32 bounds)
where TBitVector2D : IBitVector2D
where TCheckColorFn : IFunc<ColorBgra, ColorBgra, byte, bool>
{
Validate.Begin()
.IsNotNull(sampleSource, nameof(sampleSource))
.IsNotNull(cancelToken, nameof(cancelToken))
.Check();
using (Systrace.BeginEvent(nameof(FloodFillAlgorithm) + "::" + nameof(FillStencilFromPoint)))
{
cancelToken.ThrowIfCancellationRequested();
int srcWidth = sampleSource.Width;
int srcHeight = sampleSource.Height;
if (srcWidth > stencilBuffer.Width || srcHeight > stencilBuffer.Height)
{
throw new ArgumentException();
}
if (!sampleSource.CheckPointValue(startPt))
{
bounds = RectInt32.Empty;
return;
}
int width = sampleSource.Width;
int height = sampleSource.Height;
using RendererRowCache<ColorBgra> rowCache = new RendererRowCache<ColorBgra>(sampleSource);
int boundsLeft = int.MaxValue;
int boundsTop = int.MaxValue;
int boundsRight = int.MinValue;
int boundsBottom = int.MinValue;
Queue<Point2Int32> queue = new Queue<Point2Int32>(16);
queue.Enqueue(startPt);
rowCache.AddRowRef(startPt.Y - 1);
rowCache.AddRowRef(startPt.Y);
rowCache.AddRowRef(startPt.Y + 1);
ColorBgra basis = ((ColorBgra*)rowCache.GetRow(startPt.Y))[startPt.X];
while (queue.Any())
{
cancelToken.ThrowIfCancellationRequested();
Point2Int32 pt = queue.Dequeue();
try
{
if (!sampleSource.CheckPointValue(pt))
{
continue;
}
ColorBgra* pRow = (ColorBgra*)rowCache.GetRow(pt.Y);
BitSpan stencilRow = stencilBuffer.GetRowSpanUnchecked(pt.Y);
int localLeft = pt.X - 1;
int localRight = pt.X;
while (localLeft >= 0 &&
!stencilRow.GetUnchecked(localLeft) &&
checkColorFn.Invoke(basis, pRow[localLeft], tolerance))
{
stencilRow.SetUnchecked(localLeft, true);
--localLeft;
}
while (localRight < width &&
!stencilRow.GetUnchecked(localRight) &&
checkColorFn.Invoke(basis, pRow[localRight], tolerance))
{
stencilRow.SetUnchecked(localRight, true);
++localRight;
}
++localLeft;
--localRight;
if (pt.Y > 0)
{
rowCache.AddRowRef(pt.Y - 2);
rowCache.AddRowRef(pt.Y - 1);
rowCache.AddRowRef(pt.Y);
try
{
ColorBgra* pRowUp = (ColorBgra*)rowCache.GetRow(pt.Y - 1);
BitSpan stencilRowUp = stencilBuffer.GetRowSpanUnchecked(pt.Y - 1);
int sleft = localLeft;
int sright = localLeft;
for (int sx = localLeft; sx <= localRight; ++sx)
{
if (!stencilRowUp.GetUnchecked(sx) &&
checkColorFn.Invoke(basis, pRowUp[sx], tolerance))
{
++sright;
}
else
{
if ((sright - sleft) > 0)
{
queue.Enqueue(new Point2Int32(sleft, pt.Y - 1));
rowCache.AddRowRef(pt.Y - 2);
rowCache.AddRowRef(pt.Y - 1);
rowCache.AddRowRef(pt.Y);
}
++sright;
sleft = sright;
}
}
if ((sright - sleft) > 0)
{
queue.Enqueue(new Point2Int32(sleft, pt.Y - 1));
rowCache.AddRowRef(pt.Y - 2);
rowCache.AddRowRef(pt.Y - 1);
rowCache.AddRowRef(pt.Y);
}
}
finally
{
rowCache.ReleaseRowRef(pt.Y - 2);
rowCache.ReleaseRowRef(pt.Y - 1);
rowCache.ReleaseRowRef(pt.Y);
}
}
if (pt.Y < (height - 1))
{
rowCache.AddRowRef(pt.Y);
rowCache.AddRowRef(pt.Y + 1);
rowCache.AddRowRef(pt.Y + 2);
try
{
ColorBgra* pRowDown = (ColorBgra*)rowCache.GetRow(pt.Y + 1);
BitSpan stencilRowDown = stencilBuffer.GetRowSpanUnchecked(pt.Y + 1);
int sleft = localLeft;
int sright = localLeft;
for (int sx = localLeft; sx <= localRight; ++sx)
{
if (!stencilRowDown.GetUnchecked(sx) &&
checkColorFn.Invoke(basis, pRowDown[sx], tolerance))
{
++sright;
}
else
{
if ((sright - sleft) > 0)
{
queue.Enqueue(new Point2Int32(sleft, pt.Y + 1));
rowCache.AddRowRef(pt.Y);
rowCache.AddRowRef(pt.Y + 1);
rowCache.AddRowRef(pt.Y + 2);
}
++sright;
sleft = sright;
}
}
if ((sright - sleft) > 0)
{
queue.Enqueue(new Point2Int32(sleft, pt.Y + 1));
rowCache.AddRowRef(pt.Y);
rowCache.AddRowRef(pt.Y + 1);
rowCache.AddRowRef(pt.Y + 2);
}
}
finally
{
rowCache.ReleaseRowRef(pt.Y);
rowCache.ReleaseRowRef(pt.Y + 1);
rowCache.ReleaseRowRef(pt.Y + 2);
}
}
boundsLeft = Math.Min(boundsLeft, localLeft);
boundsTop = Math.Min(boundsTop, pt.Y);
boundsRight = Math.Max(boundsRight, localRight);
boundsBottom = Math.Max(boundsBottom, pt.Y);
}
finally
{
rowCache.ReleaseRowRef(pt.Y - 1);
rowCache.ReleaseRowRef(pt.Y);
rowCache.ReleaseRowRef(pt.Y + 1);
}
}
#if DEBUG
for (int row = 0; row < height; ++row)
{
Debug.Assert(rowCache.GetRowRefCount(row) == 0);
Debug.Assert(!rowCache.IsRowCached(row));
}
#endif
bounds = RectInt32.FromEdges(boundsLeft, boundsTop, boundsRight, boundsBottom);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment