Created
August 6, 2021 16:27
-
-
Save rickbrew/4146e7baa050a0cc09a19f1acef6f340 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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