Skip to content

Commit 631a9d3

Browse files
committed
SharedLib: NpcNameFinder: Sliced into multiple files. DetermineNpcs: early return. PopulateLines: Parallel.For body only captures local variables. Using Interlocked.Add for incrementing 'i'
1 parent 20d977f commit 631a9d3

File tree

6 files changed

+142
-163
lines changed

6 files changed

+142
-163
lines changed

SharedLib/NpcFinder/LineSegment.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
namespace SharedLib.NpcFinder;
22

3-
public readonly struct LineSegment
3+
public readonly record struct LineSegment
44
{
5-
public readonly int XStart;
5+
public readonly int X;
66
public readonly int Y;
7-
public readonly int XEnd;
8-
public readonly int XCenter;
7+
8+
public readonly int XStart => X & 0xFFFF;
9+
public readonly int XEnd => X >> 16;
10+
public readonly int XCenter => XStart + ((XEnd - XStart) / 2);
911

1012
public LineSegment(int xStart, int xEnd, int y)
1113
{
12-
this.XStart = xStart;
13-
this.Y = y;
14-
this.XEnd = xEnd;
15-
XCenter = XStart + ((XEnd - XStart) / 2);
14+
X = (xEnd << 16) | (xStart & 0xFFFF);
15+
Y = y;
1616
}
1717
}

SharedLib/NpcFinder/NpcNameColors.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
namespace SharedLib.NpcFinder;
2+
3+
public static class NpcNameColors
4+
{
5+
public const byte fBase = 230;
6+
7+
public const byte fE_R = fBase;
8+
public const byte fE_G = 0;
9+
public const byte fE_B = 0;
10+
11+
public const byte fF_R = 0;
12+
public const byte fF_G = fBase;
13+
public const byte fF_B = 0;
14+
15+
public const byte fN_R = fBase;
16+
public const byte fN_G = fBase;
17+
public const byte fN_B = 0;
18+
19+
public const byte fuzzCorpse = 18;
20+
public const byte fC_RGB = 128;
21+
22+
public const byte sE_R = 240;
23+
public const byte sE_G = 35;
24+
public const byte sE_B = 35;
25+
26+
public const byte sF_R = 0;
27+
public const byte sF_G = 250;
28+
public const byte sF_B = 0;
29+
30+
public const byte sN_R = 250;
31+
public const byte sN_G = 250;
32+
public const byte sN_B = 0;
33+
34+
public const byte sNamePlate_N = 254;
35+
36+
public const byte sNamePlate_H_R = 254;
37+
public const byte sNamePlate_H_G = 254;
38+
public const byte sNamePlate_H_B = 0;
39+
}

SharedLib/NpcFinder/NpcNameFinder.cs

Lines changed: 41 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -8,65 +8,20 @@
88
using System.Runtime.CompilerServices;
99
using System.Buffers;
1010
using System.Collections.Generic;
11+
using System.Threading;
1112

12-
#pragma warning disable 162
13+
using static SharedLib.NpcFinder.NpcNameColors;
1314

1415
namespace SharedLib.NpcFinder;
1516

16-
[Flags]
17-
public enum NpcNames
18-
{
19-
None = 0,
20-
Enemy = 1,
21-
Friendly = 2,
22-
Neutral = 4,
23-
Corpse = 8,
24-
NamePlate = 16
25-
}
26-
27-
public static class NpcNames_Extension
28-
{
29-
public static string ToStringF(this NpcNames value) => value switch
30-
{
31-
NpcNames.None => nameof(NpcNames.None),
32-
NpcNames.Enemy => nameof(NpcNames.Enemy),
33-
NpcNames.Friendly => nameof(NpcNames.Friendly),
34-
NpcNames.Neutral => nameof(NpcNames.Neutral),
35-
NpcNames.Corpse => nameof(NpcNames.Corpse),
36-
NpcNames.NamePlate => nameof(NpcNames.NamePlate),
37-
_ => nameof(NpcNames.None),
38-
};
39-
40-
public static bool HasFlagF(this NpcNames value, NpcNames flag)
41-
{
42-
return (value & flag) != 0;
43-
}
44-
}
45-
46-
public enum SearchMode
47-
{
48-
Simple = 0,
49-
Fuzzy = 1
50-
}
51-
52-
public static class SearchMode_Extension
53-
{
54-
public static string ToStringF(this SearchMode value) => value switch
55-
{
56-
SearchMode.Simple => nameof(SearchMode.Simple),
57-
SearchMode.Fuzzy => nameof(SearchMode.Fuzzy),
58-
_ => nameof(SearchMode.Simple),
59-
};
60-
}
61-
6217
public sealed partial class NpcNameFinder : IDisposable
6318
{
6419
private readonly ILogger logger;
6520
private readonly IBitmapProvider bitmapProvider;
6621
private readonly PixelFormat pixelFormat;
6722
private readonly INpcResetEvent resetEvent;
6823

69-
private readonly int bytesPerPixel;
24+
private const int bytesPerPixel = 4;
7025

7126
private readonly Pen whitePen;
7227
private readonly Pen greyPen;
@@ -104,76 +59,23 @@ public sealed partial class NpcNameFinder : IDisposable
10459

10560
private readonly NpcPositionComparer npcPosComparer;
10661

107-
#region variables
108-
109-
public float colorFuzziness { get; set; } = 15f;
110-
11162
private const int colorFuzz = 40;
112-
113-
public int topOffset { get; set; } = 117;
63+
private const int topOffset = 117;
64+
public int WidthDiff { get; set; } = 4;
11465

11566
private float heightMul;
11667
public int HeightMulti { get; set; }
117-
118-
public int MaxWidth { get; set; } = 250;
119-
12068
public int MinHeight { get; set; } = 16;
121-
122-
public int WidthDiff { get; set; } = 4;
123-
12469
public int HeightOffset1 { get; set; } = 10;
125-
12670
public int HeightOffset2 { get; set; } = 2;
12771

128-
#endregion
129-
130-
#region Colors
131-
132-
public const byte fBase = 230;
133-
134-
public const byte fE_R = fBase;
135-
public const byte fE_G = 0;
136-
public const byte fE_B = 0;
137-
138-
public const byte fF_R = 0;
139-
public const byte fF_G = fBase;
140-
public const byte fF_B = 0;
141-
142-
public const byte fN_R = fBase;
143-
public const byte fN_G = fBase;
144-
public const byte fN_B = 0;
145-
146-
public const byte fuzzCorpse = 18;
147-
public const byte fC_RGB = 128;
148-
149-
public const byte sE_R = 240;
150-
public const byte sE_G = 35;
151-
public const byte sE_B = 35;
152-
153-
public const byte sF_R = 0;
154-
public const byte sF_G = 250;
155-
public const byte sF_B = 0;
156-
157-
public const byte sN_R = 250;
158-
public const byte sN_G = 250;
159-
public const byte sN_B = 0;
160-
161-
public const byte sNamePlate_N = 254;
162-
163-
public const byte sNamePlate_H_R = 254;
164-
public const byte sNamePlate_H_G = 254;
165-
public const byte sNamePlate_H_B = 0;
166-
167-
#endregion
168-
16972
public NpcNameFinder(ILogger logger, IBitmapProvider bitmapProvider,
17073
INpcResetEvent resetEvent)
17174
{
17275
this.logger = logger;
17376
this.bitmapProvider = bitmapProvider;
17477
this.pixelFormat = bitmapProvider.Bitmap.PixelFormat;
17578
this.resetEvent = resetEvent;
176-
this.bytesPerPixel = Bitmap.GetPixelFormatSize(pixelFormat) / 8;
17779

17880
UpdateSearchMode();
17981

@@ -447,7 +349,7 @@ public void Update()
447349
resetEvent.Reset();
448350

449351
ReadOnlySpan<LineSegment> lineSegments =
450-
PopulateLines(bitmapProvider.Bitmap, Area);
352+
PopulateLines(bitmapProvider.Bitmap, Area, colorMatcher, Area, ScaleWidth(MinHeight), ScaleWidth(WidthDiff));
451353
Npcs = DetermineNpcs(lineSegments);
452354

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

518-
if (gc > 0)
519-
{
520-
ref LineSegment n = ref group[0];
521-
Rectangle rect = new(n.XStart, n.Y, n.XEnd - n.XStart, 1);
420+
if (gc <= 0)
421+
continue;
522422

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

527-
rect.X = Math.Min(rect.X, n.XStart);
528-
rect.Y = Math.Min(rect.Y, n.Y);
426+
for (int g = 1; g < gc; g++)
427+
{
428+
n = group[g];
529429

530-
if (rect.Right < n.XEnd)
531-
rect.Width = n.XEnd - n.XStart;
430+
rect.X = Math.Min(rect.X, n.XStart);
431+
rect.Y = Math.Min(rect.Y, n.Y);
532432

533-
if (rect.Bottom < n.Y)
534-
rect.Height = n.Y - rect.Y;
535-
}
536-
int yOffset = YOffset(Area, rect);
537-
npcs[c++] = new NpcPosition(
538-
rect.Location, rect.Max(), yOffset, heightMul);
433+
if (rect.Right < n.XEnd)
434+
rect.Width = n.XEnd - n.XStart;
435+
436+
if (rect.Bottom < n.Y)
437+
rect.Height = n.Y - rect.Y;
539438
}
439+
int yOffset = YOffset(Area, rect);
440+
npcs[c++] = new NpcPosition(
441+
rect.Location, rect.Max(), yOffset, heightMul);
540442
}
541443

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

544-
for (int i = 0; i < c; i++)
446+
for (int i = 0; i < c - 1; i++)
545447
{
546448
ref readonly NpcPosition ii = ref npcs[i];
547449
if (ii.Equals(NpcPosition.Empty))
548450
continue;
549451

550-
for (int j = 0; j < c; j++)
452+
for (int j = i + 1; j < c; j++)
551453
{
552454
ref readonly NpcPosition jj = ref npcs[j];
553-
if (i == j || jj.Equals(NpcPosition.Empty))
455+
if (jj.Equals(NpcPosition.Empty))
554456
continue;
555457

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

611-
private bool IsAdd(NpcPosition c)
513+
public bool IsAdd(NpcPosition c)
612514
{
613515
return
614516
(c.ClickPoint.X < screenMid - screenTargetBuffer &&
@@ -623,21 +525,19 @@ private int YOffset(Rectangle area, Rectangle npc)
623525
}
624526

625527
[SkipLocalsInit]
626-
private ReadOnlySpan<LineSegment> PopulateLines(Bitmap bitmap, Rectangle rect)
528+
private ReadOnlySpan<LineSegment> PopulateLines(Bitmap bitmap, Rectangle rect,
529+
Func<byte, byte, byte, bool> colorMatcher, Rectangle area, float minLength, float lengthDiff)
627530
{
628-
Rectangle area = this.Area;
629-
int bytesPerPixel = this.bytesPerPixel;
630-
631-
int width = (area.Right - area.Left) / 32;
632-
int height = (area.Bottom - area.Top) / 32;
531+
const int RESOLUTION = 64;
532+
int width = (area.Right - area.Left) / RESOLUTION;
533+
int height = (area.Bottom - area.Top) / RESOLUTION;
633534
int size = width * height;
535+
634536
var pooler = ArrayPool<LineSegment>.Shared;
635537
LineSegment[] segments = pooler.Rent(size);
636538
int i = 0;
637539

638-
Func<byte, byte, byte, bool> colorMatcher = this.colorMatcher;
639-
float minLength = ScaleWidth(MinHeight);
640-
float lengthDiff = ScaleWidth(WidthDiff);
540+
int end = area.Right;
641541
float minEndLength = minLength - lengthDiff;
642542

643543
BitmapData bitmapData =
@@ -647,9 +547,8 @@ private ReadOnlySpan<LineSegment> PopulateLines(Bitmap bitmap, Rectangle rect)
647547
[SkipLocalsInit]
648548
unsafe void body(int y)
649549
{
650-
int xStart = -1;
651-
int xEnd = -1;
652-
int end = area.Right;
550+
int xStart = int.MinValue;
551+
int xEnd = int.MinValue;
653552

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

665-
if (xStart > -1 && (x - xEnd) < minLength)
564+
if (xStart > int.MinValue && (x - xEnd) < minLength)
666565
{
667566
xEnd = x;
668567
}
669568
else
670569
{
671-
if (xStart > -1 && xEnd - xStart > minEndLength)
570+
if (xStart > int.MinValue && xEnd - xStart > minEndLength)
672571
{
673572
if (i + 1 >= size)
674573
return;
675574

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

679578
xStart = x;
680579
}
681580
xEnd = x;
682581
}
683582

684-
if (i < size && xStart > -1 && xEnd - xStart > minEndLength)
583+
if (xStart > int.MinValue && xEnd - xStart > minEndLength)
685584
{
686-
segments[i++] = new LineSegment(xStart, xEnd, y);
585+
segments[Interlocked.Add(ref i, 1)] = new LineSegment(xStart, xEnd, y);
687586
}
688587
}
689588
_ = Parallel.For(area.Top, area.Height, body);

0 commit comments

Comments
 (0)