Skip to content

Commit

Permalink
SharedLib: NpcNameFinder: Sliced into multiple files. DetermineNpcs: …
Browse files Browse the repository at this point in the history
…early return. PopulateLines: Parallel.For body only captures local variables. Using Interlocked.Add for incrementing 'i'
  • Loading branch information
Xian55 committed Oct 19, 2023
1 parent 20d977f commit 631a9d3
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 163 deletions.
16 changes: 8 additions & 8 deletions SharedLib/NpcFinder/LineSegment.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
namespace SharedLib.NpcFinder;

public readonly struct LineSegment
public readonly record struct LineSegment
{
public readonly int XStart;
public readonly int X;
public readonly int Y;
public readonly int XEnd;
public readonly int XCenter;

public readonly int XStart => X & 0xFFFF;
public readonly int XEnd => X >> 16;
public readonly int XCenter => XStart + ((XEnd - XStart) / 2);

public LineSegment(int xStart, int xEnd, int y)
{
this.XStart = xStart;
this.Y = y;
this.XEnd = xEnd;
XCenter = XStart + ((XEnd - XStart) / 2);
X = (xEnd << 16) | (xStart & 0xFFFF);
Y = y;
}
}
39 changes: 39 additions & 0 deletions SharedLib/NpcFinder/NpcNameColors.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
namespace SharedLib.NpcFinder;

public static class NpcNameColors
{
public const byte fBase = 230;

public const byte fE_R = fBase;
public const byte fE_G = 0;
public const byte fE_B = 0;

public const byte fF_R = 0;
public const byte fF_G = fBase;
public const byte fF_B = 0;

public const byte fN_R = fBase;
public const byte fN_G = fBase;
public const byte fN_B = 0;

public const byte fuzzCorpse = 18;
public const byte fC_RGB = 128;

public const byte sE_R = 240;
public const byte sE_G = 35;
public const byte sE_B = 35;

public const byte sF_R = 0;
public const byte sF_G = 250;
public const byte sF_B = 0;

public const byte sN_R = 250;
public const byte sN_G = 250;
public const byte sN_B = 0;

public const byte sNamePlate_N = 254;

public const byte sNamePlate_H_R = 254;
public const byte sNamePlate_H_G = 254;
public const byte sNamePlate_H_B = 0;
}
183 changes: 41 additions & 142 deletions SharedLib/NpcFinder/NpcNameFinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,65 +8,20 @@
using System.Runtime.CompilerServices;
using System.Buffers;
using System.Collections.Generic;
using System.Threading;

#pragma warning disable 162
using static SharedLib.NpcFinder.NpcNameColors;

namespace SharedLib.NpcFinder;

[Flags]
public enum NpcNames
{
None = 0,
Enemy = 1,
Friendly = 2,
Neutral = 4,
Corpse = 8,
NamePlate = 16
}

public static class NpcNames_Extension
{
public static string ToStringF(this NpcNames value) => value switch
{
NpcNames.None => nameof(NpcNames.None),
NpcNames.Enemy => nameof(NpcNames.Enemy),
NpcNames.Friendly => nameof(NpcNames.Friendly),
NpcNames.Neutral => nameof(NpcNames.Neutral),
NpcNames.Corpse => nameof(NpcNames.Corpse),
NpcNames.NamePlate => nameof(NpcNames.NamePlate),
_ => nameof(NpcNames.None),
};

public static bool HasFlagF(this NpcNames value, NpcNames flag)
{
return (value & flag) != 0;
}
}

public enum SearchMode
{
Simple = 0,
Fuzzy = 1
}

public static class SearchMode_Extension
{
public static string ToStringF(this SearchMode value) => value switch
{
SearchMode.Simple => nameof(SearchMode.Simple),
SearchMode.Fuzzy => nameof(SearchMode.Fuzzy),
_ => nameof(SearchMode.Simple),
};
}

public sealed partial class NpcNameFinder : IDisposable
{
private readonly ILogger logger;
private readonly IBitmapProvider bitmapProvider;
private readonly PixelFormat pixelFormat;
private readonly INpcResetEvent resetEvent;

private readonly int bytesPerPixel;
private const int bytesPerPixel = 4;

private readonly Pen whitePen;
private readonly Pen greyPen;
Expand Down Expand Up @@ -104,76 +59,23 @@ public sealed partial class NpcNameFinder : IDisposable

private readonly NpcPositionComparer npcPosComparer;

#region variables

public float colorFuzziness { get; set; } = 15f;

private const int colorFuzz = 40;

public int topOffset { get; set; } = 117;
private const int topOffset = 117;
public int WidthDiff { get; set; } = 4;

private float heightMul;
public int HeightMulti { get; set; }

public int MaxWidth { get; set; } = 250;

public int MinHeight { get; set; } = 16;

public int WidthDiff { get; set; } = 4;

public int HeightOffset1 { get; set; } = 10;

public int HeightOffset2 { get; set; } = 2;

#endregion

#region Colors

public const byte fBase = 230;

public const byte fE_R = fBase;
public const byte fE_G = 0;
public const byte fE_B = 0;

public const byte fF_R = 0;
public const byte fF_G = fBase;
public const byte fF_B = 0;

public const byte fN_R = fBase;
public const byte fN_G = fBase;
public const byte fN_B = 0;

public const byte fuzzCorpse = 18;
public const byte fC_RGB = 128;

public const byte sE_R = 240;
public const byte sE_G = 35;
public const byte sE_B = 35;

public const byte sF_R = 0;
public const byte sF_G = 250;
public const byte sF_B = 0;

public const byte sN_R = 250;
public const byte sN_G = 250;
public const byte sN_B = 0;

public const byte sNamePlate_N = 254;

public const byte sNamePlate_H_R = 254;
public const byte sNamePlate_H_G = 254;
public const byte sNamePlate_H_B = 0;

#endregion

public NpcNameFinder(ILogger logger, IBitmapProvider bitmapProvider,
INpcResetEvent resetEvent)
{
this.logger = logger;
this.bitmapProvider = bitmapProvider;
this.pixelFormat = bitmapProvider.Bitmap.PixelFormat;
this.resetEvent = resetEvent;
this.bytesPerPixel = Bitmap.GetPixelFormatSize(pixelFormat) / 8;

UpdateSearchMode();

Expand Down Expand Up @@ -447,7 +349,7 @@ public void Update()
resetEvent.Reset();

ReadOnlySpan<LineSegment> lineSegments =
PopulateLines(bitmapProvider.Bitmap, Area);
PopulateLines(bitmapProvider.Bitmap, Area, colorMatcher, Area, ScaleWidth(MinHeight), ScaleWidth(WidthDiff));
Npcs = DetermineNpcs(lineSegments);

TargetCount = Npcs.Count(TargetsCount);
Expand Down Expand Up @@ -515,42 +417,42 @@ private ArraySegment<NpcPosition> DetermineNpcs(ReadOnlySpan<LineSegment> data)
group[gc++] = laterNpcLine;
}

if (gc > 0)
{
ref LineSegment n = ref group[0];
Rectangle rect = new(n.XStart, n.Y, n.XEnd - n.XStart, 1);
if (gc <= 0)
continue;

for (int g = 1; g < gc; g++)
{
n = group[g];
ref LineSegment n = ref group[0];
Rectangle rect = new(n.XStart, n.Y, n.XEnd - n.XStart, 1);

rect.X = Math.Min(rect.X, n.XStart);
rect.Y = Math.Min(rect.Y, n.Y);
for (int g = 1; g < gc; g++)
{
n = group[g];

if (rect.Right < n.XEnd)
rect.Width = n.XEnd - n.XStart;
rect.X = Math.Min(rect.X, n.XStart);
rect.Y = Math.Min(rect.Y, n.Y);

if (rect.Bottom < n.Y)
rect.Height = n.Y - rect.Y;
}
int yOffset = YOffset(Area, rect);
npcs[c++] = new NpcPosition(
rect.Location, rect.Max(), yOffset, heightMul);
if (rect.Right < n.XEnd)
rect.Width = n.XEnd - n.XStart;

if (rect.Bottom < n.Y)
rect.Height = n.Y - rect.Y;
}
int yOffset = YOffset(Area, rect);
npcs[c++] = new NpcPosition(
rect.Location, rect.Max(), yOffset, heightMul);
}

int lineHeight = 2 * (int)ScaleHeight(MinHeight);

for (int i = 0; i < c; i++)
for (int i = 0; i < c - 1; i++)
{
ref readonly NpcPosition ii = ref npcs[i];
if (ii.Equals(NpcPosition.Empty))
continue;

for (int j = 0; j < c; j++)
for (int j = i + 1; j < c; j++)
{
ref readonly NpcPosition jj = ref npcs[j];
if (i == j || jj.Equals(NpcPosition.Empty))
if (jj.Equals(NpcPosition.Empty))
continue;

Point pi = ii.Rect.Centre();
Expand Down Expand Up @@ -608,7 +510,7 @@ private bool TargetsCount(NpcPosition c)
Math.Abs(c.ClickPoint.X - screenMid) < screenTargetBuffer;
}

private bool IsAdd(NpcPosition c)
public bool IsAdd(NpcPosition c)
{
return
(c.ClickPoint.X < screenMid - screenTargetBuffer &&
Expand All @@ -623,21 +525,19 @@ private int YOffset(Rectangle area, Rectangle npc)
}

[SkipLocalsInit]
private ReadOnlySpan<LineSegment> PopulateLines(Bitmap bitmap, Rectangle rect)
private ReadOnlySpan<LineSegment> PopulateLines(Bitmap bitmap, Rectangle rect,
Func<byte, byte, byte, bool> colorMatcher, Rectangle area, float minLength, float lengthDiff)
{
Rectangle area = this.Area;
int bytesPerPixel = this.bytesPerPixel;

int width = (area.Right - area.Left) / 32;
int height = (area.Bottom - area.Top) / 32;
const int RESOLUTION = 64;
int width = (area.Right - area.Left) / RESOLUTION;
int height = (area.Bottom - area.Top) / RESOLUTION;
int size = width * height;

var pooler = ArrayPool<LineSegment>.Shared;
LineSegment[] segments = pooler.Rent(size);
int i = 0;

Func<byte, byte, byte, bool> colorMatcher = this.colorMatcher;
float minLength = ScaleWidth(MinHeight);
float lengthDiff = ScaleWidth(WidthDiff);
int end = area.Right;
float minEndLength = minLength - lengthDiff;

BitmapData bitmapData =
Expand All @@ -647,9 +547,8 @@ private ReadOnlySpan<LineSegment> PopulateLines(Bitmap bitmap, Rectangle rect)
[SkipLocalsInit]
unsafe void body(int y)
{
int xStart = -1;
int xEnd = -1;
int end = area.Right;
int xStart = int.MinValue;
int xEnd = int.MinValue;

byte* currentLine = (byte*)bitmapData.Scan0 + (y * bitmapData.Stride);
for (int x = area.Left; x < end; x++)
Expand All @@ -662,28 +561,28 @@ unsafe void body(int y)
currentLine[xi]))
continue;

if (xStart > -1 && (x - xEnd) < minLength)
if (xStart > int.MinValue && (x - xEnd) < minLength)
{
xEnd = x;
}
else
{
if (xStart > -1 && xEnd - xStart > minEndLength)
if (xStart > int.MinValue && xEnd - xStart > minEndLength)
{
if (i + 1 >= size)
return;

segments[i++] = new LineSegment(xStart, xEnd, y);
segments[Interlocked.Add(ref i, 1)] = new LineSegment(xStart, xEnd, y);
}

xStart = x;
}
xEnd = x;
}

if (i < size && xStart > -1 && xEnd - xStart > minEndLength)
if (xStart > int.MinValue && xEnd - xStart > minEndLength)
{
segments[i++] = new LineSegment(xStart, xEnd, y);
segments[Interlocked.Add(ref i, 1)] = new LineSegment(xStart, xEnd, y);
}
}
_ = Parallel.For(area.Top, area.Height, body);
Expand Down
Loading

0 comments on commit 631a9d3

Please sign in to comment.