diff --git a/SINoCOLO/MainForm.cs b/SINoCOLO/MainForm.cs index 8d6cac1..a8dd529 100644 --- a/SINoCOLO/MainForm.cs +++ b/SINoCOLO/MainForm.cs @@ -17,7 +17,8 @@ public partial class MainForm : Form private bool hasDetailCtrl = true; private bool selectInstanceMode = false; private int numHighFreqTicks = 0; - private int numScanDelayTicks = 0; + private int numScanProcessDelayTicks = 0; + private int numScanSnapshotDelayTicks = 0; private int numTicksToResetStoryMode = -1; private string cachedTitle; @@ -120,6 +121,10 @@ private void GameLogic_OnStateChangeNotify(GameLogic.EState newState) private void Scan() { gameLogic.OnScanPrep(); + if (numScanSnapshotDelayTicks > 0) + { + return; + } var srcScreenshot = screenReader.DoWork(); if (srcScreenshot != null) @@ -128,7 +133,7 @@ private void Scan() cachedSourceScreen = srcScreenshot.Clone(new Rectangle(0, 0, srcScreenshot.Width, srcScreenshot.Height), srcScreenshot.PixelFormat); if (screenReader.GetState() != ScreenReader.EState.WindowTooSmall && - numScanDelayTicks <= 0) + (numScanProcessDelayTicks <= 0)) { var forcedSize = screenReader.GetExpectedSize(); var fastBitmap = ScreenshotUtilities.ConvertToFastBitmap(srcScreenshot, forcedSize.Width, forcedSize.Height); @@ -387,10 +392,20 @@ private void UpdateTimerFreq() } } - numScanDelayTicks -= 1; - if (numScanDelayTicks < 0) + numScanProcessDelayTicks -= 1; + if (numScanProcessDelayTicks < 0) + { + // high freq: process in every timer tick, 10x per second + // otherwise: run recognition once every 1s + numScanProcessDelayTicks = (numHighFreqTicks > 0) ? 0 : 10; + } + + numScanSnapshotDelayTicks -= 1; + if (numScanSnapshotDelayTicks < 0) { - numScanDelayTicks = (numHighFreqTicks > 0) ? 0 : 20; + // can click: take screenshot in every timer tick, 10x per second + // otherwise: grab window once every 0.5s + numScanSnapshotDelayTicks = checkBoxClicks.Checked ? 0 : 5; } } @@ -402,8 +417,9 @@ private void DetailLog() } var lines = new List(); - lines.Add(string.Format("Tick: high freq:{0}, delay:{1}{2}", - numHighFreqTicks, numScanDelayTicks, numScanDelayTicks <= 0 ? " (scan now)" : "")); + lines.Add(string.Format("Tick: HFreq:{0}, snap:{1}, process:{2} {3}", + numHighFreqTicks, numScanSnapshotDelayTicks, numScanProcessDelayTicks, + (numScanProcessDelayTicks <= 0 && numScanSnapshotDelayTicks <= 0) ? " (scan now)" : "")); lines.Add(string.Format("Screenshot:{0} ({1})", cachedSourceScreen != null ? string.Format("{0}x{1}", cachedSourceScreen.Width, cachedSourceScreen.Height) : "n/a", screenReader.GetState())); diff --git a/SINoCOLO/Properties/AssemblyInfo.cs b/SINoCOLO/Properties/AssemblyInfo.cs index e397d60..2a764ad 100644 --- a/SINoCOLO/Properties/AssemblyInfo.cs +++ b/SINoCOLO/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("26.0.0.0")] -[assembly: AssemblyFileVersion("26.0.0.0")] +[assembly: AssemblyVersion("27.0.0.0")] +[assembly: AssemblyFileVersion("27.0.0.0")] diff --git a/SINoCOLO/ScreenReader.cs b/SINoCOLO/ScreenReader.cs index ec85569..ccc06a0 100644 --- a/SINoCOLO/ScreenReader.cs +++ b/SINoCOLO/ScreenReader.cs @@ -393,7 +393,7 @@ private Bitmap TakeScreenshot() Bitmap scaledBitmap = new Bitmap(finalSize.Width, finalSize.Height); using (Graphics g = Graphics.FromImage(scaledBitmap)) { - g.InterpolationMode = InterpolationMode.HighQualityBicubic; + g.InterpolationMode = InterpolationMode.Bilinear; g.DrawImage(bitmap, 0, 0, finalSize.Width, finalSize.Height); } diff --git a/SINoVision/ScannerBase.cs b/SINoVision/ScannerBase.cs index 225e54e..b2fe06a 100644 --- a/SINoVision/ScannerBase.cs +++ b/SINoVision/ScannerBase.cs @@ -150,14 +150,14 @@ protected void DrawRectangle(FastBitmapHSV bitmap, int posX, int posY, int width for (int idxX = 0; idxX < width; idxX++) { - bitmap.Pixels[(posX + idxX) + ((posY - border) * bitmap.Width)] = pixelOb; - bitmap.Pixels[(posX + idxX) + ((posY + height + border) * bitmap.Width)] = pixelOb; + bitmap.SetPixel(posX + idxX, posY - border, pixelOb); + bitmap.SetPixel(posX + idxX, posY + height + border, pixelOb); } for (int idxY = 0; idxY < height; idxY++) { - bitmap.Pixels[(posX - border) + ((posY + idxY) * bitmap.Width)] = pixelOb; - bitmap.Pixels[(posX + width + border) + ((posY + idxY) * bitmap.Width)] = pixelOb; + bitmap.SetPixel(posX - border, posY + idxY, pixelOb); + bitmap.SetPixel(posX + width + border, posY + idxY, pixelOb); } } } diff --git a/SINoVision/ScannerColoPurify.cs b/SINoVision/ScannerColoPurify.cs index 771c578..25fb9d8 100644 --- a/SINoVision/ScannerColoPurify.cs +++ b/SINoVision/ScannerColoPurify.cs @@ -331,7 +331,7 @@ protected void ScanActionSlot(FastBitmapHSV bitmap, Point slotPos, ScreenData sc byte color = (byte)(pixelInput[readIdx] * 255); readIdx++; - bitmap.Pixels[(previewX + idxX) + ((previewY + idxY) * bitmap.Width)] = new FastPixelHSV(color, color, color); + bitmap.SetPixel(previewX + idxX, previewY + idxY, new FastPixelHSV(color, color, color)); } } } diff --git a/SINoVision/ScreenshotUtilities.cs b/SINoVision/ScreenshotUtilities.cs index 7438a7a..bcb13be 100644 --- a/SINoVision/ScreenshotUtilities.cs +++ b/SINoVision/ScreenshotUtilities.cs @@ -13,16 +13,21 @@ public struct FastPixelHSV public byte Value; public byte Monochrome; + public byte RawR; + public byte RawG; + public byte RawB; + public byte HasHSL; + public FastPixelHSV(byte colorR, byte colorG, byte colorB) { - Color pixelColor = Color.FromArgb(colorR, colorG, colorB); - - int Hue = (int)Math.Round(pixelColor.GetHue()); - int Saturation = (int)Math.Round(pixelColor.GetSaturation() * 100); - HuePack = (byte)(Hue & 0xff); - SaturationPack = (byte)((Saturation & 0xff) | ((Hue & 0x100) >> 1)); - Value = (byte)Math.Round(pixelColor.GetBrightness() * 100); - Monochrome = (byte)Math.Round((0.2125 * colorR) + (0.7154 * colorG) + (0.0721 * colorB)); + HuePack = 0; + SaturationPack = 0; + Value = 0; + Monochrome = 0; + RawR = colorR; + RawG = colorG; + RawB = colorB; + HasHSL = 0; } public FastPixelHSV(bool patternMatch) @@ -31,6 +36,10 @@ public FastPixelHSV(bool patternMatch) SaturationPack = 0; Value = 0; Monochrome = patternMatch ? (byte)255 : (byte)0; + RawR = Monochrome; + RawG = Monochrome; + RawB = Monochrome; + HasHSL = 1; } public int GetHue() @@ -56,11 +65,68 @@ public int GetMonochrome() public void SetHSV(int hue, int saturation, int value) { HuePack = (byte)(hue & 0xff); - SaturationPack = (byte)((saturation & 0xff) | ((hue & 0x100) >> 1)); + SaturationPack = (byte)((saturation & 0x7f) | ((hue & 0x100) >> 1)); Value = (byte)value; ScreenshotUtilities.HsvToRgb(hue, saturation, value, out int colorR, out int colorG, out int colorB); Monochrome = (byte)Math.Round((0.2125 * colorR) + (0.7154 * colorG) + (0.0721 * colorB)); + RawR = (byte)colorR; + RawG = (byte)colorG; + RawB = (byte)colorB; + HasHSL = 1; + } + + public void ExpandHSV() + { + float LinearR = (RawR / 255f); + float LinearG = (RawG / 255f); + float LinearB = (RawB / 255f); + + float MinRG = (LinearR < LinearG) ? LinearR : LinearG; + float MaxRG = (LinearR > LinearG) ? LinearR : LinearG; + float MinV = (MinRG < LinearB) ? MinRG : LinearB; + float MaxV = (MaxRG > LinearB) ? MaxRG : LinearB; + float DeltaV = MaxV - MinV; + + float H = 0; + float S = 0; + float L = (float)((MaxV + MinV) / 2.0f); + + if (DeltaV != 0) + { + if (L < 0.5f) + { + S = (float)(DeltaV / (MaxV + MinV)); + } + else + { + S = (float)(DeltaV / (2.0f - MaxV - MinV)); + } + + if (LinearR == MaxV) + { + H = (LinearG - LinearB) / DeltaV; + } + else if (LinearG == MaxV) + { + H = 2f + (LinearB - LinearR) / DeltaV; + } + else if (LinearB == MaxV) + { + H = 4f + (LinearR - LinearG) / DeltaV; + } + } + + int HueV = (int)(H * 60f); + if (HueV < 0) HueV += 360; + int SaturationV = (int)(S * 100f); + int LightV = (int)(L * 100f); + + HuePack = (byte)(HueV & 0xff); + SaturationPack = (byte)((SaturationV & 0x7f) | ((HueV & 0x100) >> 1)); + Value = (byte)LightV; + Monochrome = (byte)((0.2125 * RawR) + (0.7154 * RawG) + (0.0721 * RawB)); + HasHSL = 1; } public override string ToString() @@ -176,9 +242,32 @@ public class FastBitmapHSV public int Width; public int Height; + public FastBitmapHSV(int width, int height) + { + Width = width; + Height = height; + Pixels = new FastPixelHSV[width * height]; + } + public FastPixelHSV GetPixel(int X, int Y) { - return Pixels[X + (Y * Width)]; + int idx = X + (Y * Width); + if (Pixels[idx].HasHSL == 0) + { + Pixels[idx].ExpandHSV(); + } + + return Pixels[idx]; + } + + public void SetPixel(int X, int Y, FastPixelHSV pixel) + { + Pixels[X + (Y * Width)] = pixel; + } + + public void SetPixel(int Idx, FastPixelHSV pixel) + { + Pixels[Idx] = pixel; } public override string ToString() @@ -191,10 +280,9 @@ public class ScreenshotUtilities { public static FastBitmapHSV ConvertToFastBitmap(Bitmap image, int forcedWidth = -1, int forcedHeight = -1) { - FastBitmapHSV result = new FastBitmapHSV(); - result.Width = (forcedWidth > 0) ? forcedWidth : image.Width; - result.Height = (forcedHeight > 0) ? forcedHeight : image.Height; - result.Pixels = new FastPixelHSV[result.Width * result.Height]; + int useWidth = (forcedWidth > 0) ? forcedWidth : image.Width; + int useHeight = (forcedHeight > 0) ? forcedHeight : image.Height; + FastBitmapHSV result = new FastBitmapHSV(useWidth, useHeight); unsafe { @@ -209,7 +297,7 @@ public static FastBitmapHSV ConvertToFastBitmap(Bitmap image, int forcedWidth = int IdxPixel = IdxY * result.Width; for (int IdxByte = 0; IdxByte < bytesPerRow; IdxByte += bytesPerPixel) { - result.Pixels[IdxPixel] = new FastPixelHSV(pixels[IdxByte + 2], pixels[IdxByte + 1], pixels[IdxByte]); + result.Pixels[IdxPixel] = new FastPixelHSV(pixels[IdxByte + 2], pixels[IdxByte + 1], pixels[IdxByte]); IdxPixel++; } } @@ -222,12 +310,10 @@ public static FastBitmapHSV ConvertToFastBitmap(Bitmap image, int forcedWidth = public static FastBitmapHSV ConvertToFastBitmap(byte[] PixelsBGRA, int width, int height) { - FastBitmapHSV result = new FastBitmapHSV(); - result.Width = width; - result.Height = height; - result.Pixels = new FastPixelHSV[width * height]; + FastBitmapHSV result = new FastBitmapHSV(width, height); - for (int IdxPx = 0; IdxPx < result.Pixels.Length; IdxPx++) + int numPx = width * height; + for (int IdxPx = 0; IdxPx < numPx; IdxPx++) { int IdxByte = IdxPx * 4; result.Pixels[IdxPx] = new FastPixelHSV(PixelsBGRA[IdxByte + 2], PixelsBGRA[IdxByte + 1], PixelsBGRA[IdxByte]); @@ -760,24 +846,19 @@ public static bool CreateFloodFillBitmap(FastBitmapHSV srcBitmap, Point floodOri floodBounds = new Rectangle(minX, minY, maxX - minX + 1, maxY - minY + 1); if (floodPoints.Count > 0) { - FastPixelHSV[] bitmapPixels = new FastPixelHSV[floodBounds.Width * floodBounds.Height]; - for (int Idx = 0; Idx < bitmapPixels.Length; Idx++) + floodBitmap = new FastBitmapHSV(floodBounds.Width, floodBounds.Height); + int numPx = floodBounds.Width * floodBounds.Height; + + for (int Idx = 0; Idx < numPx; Idx++) { - bitmapPixels[Idx] = new FastPixelHSV(false); + floodBitmap.SetPixel(Idx, new FastPixelHSV(false)); } foreach (Point p in floodPoints) { int Idx = (p.X - minX) + ((p.Y - minY) * floodBounds.Width); - bitmapPixels[Idx] = new FastPixelHSV(true); + floodBitmap.SetPixel(Idx, new FastPixelHSV(true)); } - - floodBitmap = new FastBitmapHSV() - { - Pixels = bitmapPixels, - Width = floodBounds.Width, - Height = floodBounds.Height, - }; } else {