# HG changeset patch # User Jason Maltzen # Date 2019-11-02 18:41:56 # Node ID 4a851acc449154c52ccb79eb07c957684596780b # Parent 3d3789ffd348f714ffcdecb654e18706530b32b0 Fixed finding the paint bench interface with UI updates adding more shading along the borders of the color bars. Need to verify bar widths on larger interface settings, as the change to Falcon's Bait may make them wider. Also should speed up finding the paint bench, since it no longer searches every pixel on the screen one at a time until it finds the interface. diff --git a/ReactionRecorder.cs b/ReactionRecorder.cs --- a/ReactionRecorder.cs +++ b/ReactionRecorder.cs @@ -50,16 +50,16 @@ namespace DesertPaintLab }; public static readonly int[] SWATCH_WIDTH = { - 260, // tiny - 260, // small - 260, // medium - 274, // large - 304 // huge + 306, // tiny + 306, // small + 306, // medium + 320, // large + 350 // huge }; public static readonly int[] COLOR_BAR_WIDTH = { 306, // tiny - 306, // small + 306, // small -- includes left and right borders 306, // medium 320, // large 350 // huge @@ -133,6 +133,140 @@ namespace DesertPaintLab public int SwatchX { get { return lastSwatchX; } } public int SwatchY { get { return lastSwatchY; } } + private class PixelColor + { + public byte r; + public byte g; + public byte b; + + public bool IsMatch(PixelColor other) + { + return ((Math.Abs(r - other.r) <= COLOR_TOLERANCE) && + (Math.Abs(g - other.g) <= COLOR_TOLERANCE) && + (Math.Abs(b - other.b) <= COLOR_TOLERANCE)); + } + + public static bool IsDark(PixelColor color) + { + return (color.r < 0x47) && (color.g < 0x47) && (color.b < 0x47); + } + + public static bool IsPapyrus(PixelColor color) + { + // red between 208 and 244 + // 240 and 255 + // green between 192 and 237 + // 223 and 248 + // blue between 145 and 205 + // 178 and 232 + //return ((r > 0xD0) && (g >= 0xC0) && (b >= 0x91)) && + // ((r < 0xF4) && (g <= 0xED) && (b <= 0xCD)); + return ((color.r >= 0xF0) && (color.r <= 0xFF) && (color.g >= 0xDF) && (color.g <= 0xF8) && (color.b >= 0xB2) && (color.b <= 0xE8)); + } + + public static bool IsRed(PixelColor color) + { + return (color.r > 0x9F) && (color.g < 0x62) && (color.b < 0x62); + } + public static bool IsGreen(PixelColor color) + { + return (color.r < 0x62) && (color.g > 0x9F) && (color.b < 0x62); + } + public static bool IsBlue(PixelColor color) + { + return (color.r < 0x62) && (color.g < 0x62) && (color.b > 0x9F); + } + } + + unsafe private class Pixels + { + public byte* pixBytes; + public int width; + public int height; + public int stride; + public int pixelMultiplier; + + private PixelColor _tempColor = new PixelColor(); + private PixelColor _tempColor2 = new PixelColor(); + + public int ComputeOffset(int x, int y) + { + return (y * pixelMultiplier * stride) + (x * pixelMultiplier * 3); + } + + public int UpdateOffset(int offset, int xChange, int yChange) + { + return offset + (yChange * pixelMultiplier * stride) + (xChange * pixelMultiplier * 3); + } + + unsafe public void ColorAt(int offset, ref PixelColor pixel) + { + pixel.r = pixBytes[offset]; + pixel.g = pixBytes[offset + 1]; + pixel.b = pixBytes[offset + 2]; + } + + unsafe public void ColorAt(int x, int y, ref PixelColor pixel) + { + int offset = ComputeOffset(x, y); + ColorAt(offset, ref pixel); + } + + unsafe public bool DoesPixelMatch(int x, int y, Func matchFunc) + { + int offset = ComputeOffset(x, y); + ColorAt(offset, ref _tempColor); + return matchFunc(_tempColor); + } + + unsafe public bool DoesPixelMatch(int offset, Func matchFunc) + { + ColorAt(offset, ref _tempColor); + return matchFunc(_tempColor); + } + + // Compute length of horizontal bar starting at x,y using matching function + unsafe public int LengthOfColorAt(int x, int y, Func matchesColor) + { + int count = 0; + for (int xVal = x; xVal < width; ++xVal) + { + int offset = ComputeOffset(xVal, y); + if (!DoesPixelMatch(xVal, y, matchesColor)) break; + ++count; + } + return count; + } + + unsafe public bool IsSolidPatchAt(int x, int y, int patchWidth, int patchHeight) + { + if ((x + patchWidth >= width) || (y + patchHeight >= height)) return false; + ColorAt(x, y, ref _tempColor2); + bool ok = true; + ok &= DoesPixelMatch(x + patchWidth - 1, y, _tempColor2.IsMatch); + ok &= DoesPixelMatch(x, y + patchHeight - 1, _tempColor2.IsMatch); + ok &= DoesPixelMatch(x + patchWidth - 1, y + patchHeight - 1, _tempColor2.IsMatch); + for (int xOff = 1; ok && (xOff < patchWidth - 1); ++xOff) + { + ok &= DoesPixelMatch(x + xOff, y, _tempColor2.IsMatch); + ok &= DoesPixelMatch(x + xOff, y + patchHeight - 1, _tempColor2.IsMatch); + } + for (int yOff = 1; ok && (yOff < patchHeight - 1); ++yOff) + { + ok &= DoesPixelMatch(x, y + yOff, _tempColor2.IsMatch); + ok &= DoesPixelMatch(x + patchWidth - 1, y + yOff, _tempColor2.IsMatch); + } + for (int yOff = 1; ok && (yOff < patchHeight - 1); ++yOff) + { + for (int xOff = 1; ok && (xOff < patchWidth - 1); ++xOff) + { + ok &= DoesPixelMatch(x + xOff, y + yOff, _tempColor2.IsMatch); + } + } + return ok; + } + } + private static ReactionRecorder _instance; public static ReactionRecorder Instance { @@ -196,19 +330,6 @@ namespace DesertPaintLab swatchTestWidth = SWATCH_TEST_WIDTH[(int)interfaceSize] * pixelMultiplier; } - private static bool IsPapyTexture(byte r, byte g, byte b) - { - // red between 208 and 244 - // 240 and 255 - // green between 192 and 237 - // 223 and 248 - // blue between 145 and 205 - // 178 and 228 - //return ((r > 0xD0) && (g >= 0xC0) && (b >= 0x91)) && - // ((r < 0xF4) && (g <= 0xED) && (b <= 0xCD)); - return ((r >= 0xF0) && (r <= 0xFF) && (g >= 0xDF) && (g <= 0xF8) && (b >= 0xB2) && (b <= 0xE4)); - } - public void SetPixelMultiplier(int pixelMultiplier) { this.pixelMultiplier = pixelMultiplier; @@ -235,39 +356,18 @@ namespace DesertPaintLab swatchTestWidth = SWATCH_TEST_WIDTH[(int)interfaceSize] * pixelMultiplier; } - unsafe private void ColorAt(byte *pixBytes, int x, int y, int stride, out byte r, out byte g, out byte b) - { - int pixelStart = (y * stride) + (x * 3); - r = pixBytes[pixelStart]; - g = pixBytes[pixelStart + 1]; - b = pixBytes[pixelStart + 2]; - } - - private bool IsDarkPixel(int r, int g, int b) + unsafe private bool IsPossibleSwatchSlice(Pixels pixels, int x, int y) { - return ((r < 0x46) && (g < 0x46) && (b < 0x47)); - } - - private bool IsColorMatch(byte test_r, byte test_g, byte test_b, byte target_r, byte target_g, byte target_b) - { - return ((Math.Abs(test_r - target_r) <= COLOR_TOLERANCE) && - (Math.Abs(test_g - target_g) <= COLOR_TOLERANCE) && - (Math.Abs(test_b - target_b) <= COLOR_TOLERANCE)); - } - - unsafe private bool IsPossibleSwatchSlice(byte* pixBytes, int x, int y, int stride) - { - int testPixelStart = (y * stride) + (x * 3); - byte r = pixBytes[testPixelStart]; - byte g = pixBytes[testPixelStart+1]; - byte b = pixBytes[testPixelStart+2]; + int testPixelStart = pixels.ComputeOffset(x, y); + PixelColor color = new PixelColor(); + pixels.ColorAt(x, y, ref color); bool isDarkPixel = false; //bool isPapyAbove = false; //bool isPapyBelow = false; // 1.) Check if the top pixel is a dark pixel. - isDarkPixel = IsDarkPixel(r, g, b); // ((r < 0x46) && (g < 0x46) && (b < 0x46)); + isDarkPixel = PixelColor.IsDark(color); //// 2.) Check the pixel above it to see if it's from the papy texture. //int otherPixelStart = testPixelStart - stride; @@ -282,16 +382,13 @@ namespace DesertPaintLab bool result = isDarkPixel; // grab the swatch color - int swatchColorStart = testPixelStart + (2 * pixelMultiplier * stride); // 2 rows below the test pixel, skipping the faded color - r = pixBytes[swatchColorStart]; - g = pixBytes[swatchColorStart+1]; - b = pixBytes[swatchColorStart+2]; + // 2 down from top border + pixels.ColorAt(x, y + 2, ref color); - // scan the column from 2 below the top to 2 above the bottom to ensure the color matches - for (int i = (2*pixelMultiplier); result && (i < swatchHeight-(2*pixelMultiplier)); ++i) + // scan the column from 2 below the top to 3 above the bottom to ensure the color matches + for (int i = 2; result && (i < (swatchHeight-3)); ++i) { - int otherPixelStart = testPixelStart + (stride * i); - result &= IsColorMatch(pixBytes[otherPixelStart], pixBytes[otherPixelStart+1], pixBytes[otherPixelStart+2], r, g, b); + result &= pixels.DoesPixelMatch(x, y + i, color.IsMatch); } if (!result) @@ -302,61 +399,92 @@ namespace DesertPaintLab return result; } - unsafe private bool IsPossibleSwatchUpperLeft(byte* pixBytes, int x, int y, int stride) + unsafe private bool IsPossibleSwatchUpperLeft(Pixels pixels, int x, int y) { - int testPixelStart = (y * stride) + (x * 3); + int testPixelStart = pixels.ComputeOffset(x, y); - if (testPixelStart < stride) + if (testPixelStart < pixels.stride) { return false; } bool result = true; - int solidUpperLeft = testPixelStart + (2 * stride) + (2 * 3); - int solidLowerLeft = solidUpperLeft + ((swatchHeight - 4) * stride); - int solidUpperRight = solidUpperLeft + ((swatchWidth - 4) * 3); - int solidLowerRight = solidLowerLeft + ((swatchWidth - 4) * 3); - byte swatch_r = pixBytes[solidUpperLeft]; - byte swatch_g = pixBytes[solidUpperLeft + 1]; - byte swatch_b = pixBytes[solidUpperLeft + 2]; + int swatchSolidWidth = swatchWidth - 4; + int swatchSolidHeight = swatchHeight - 5; // 2 top and 3 bottom pixels are slightly different colors + int swatchSolidLeftX = x + 2; + int swatchSolidTopY = y + 2; + int swatchSolidRightX = swatchSolidLeftX + swatchSolidWidth - 1; + int swatchSolidBottomY = swatchSolidTopY + swatchSolidHeight - 1; + + PixelColor swatchColor = new PixelColor(); + pixels.ColorAt(swatchSolidLeftX, swatchSolidTopY, ref swatchColor); // Check the other 3 corners of the swatch size for color match - result &= IsColorMatch(swatch_r, swatch_r, swatch_r, pixBytes[solidUpperRight], pixBytes[solidUpperRight + 1], pixBytes[solidUpperRight + 2]); - result &= IsColorMatch(swatch_r, swatch_r, swatch_r, pixBytes[solidLowerLeft], pixBytes[solidLowerLeft + 1], pixBytes[solidLowerLeft + 2]); - result &= IsColorMatch(swatch_r, swatch_r, swatch_r, pixBytes[solidLowerRight], pixBytes[solidLowerRight + 1], pixBytes[solidLowerRight + 2]); + PixelColor testColor = new PixelColor(); + result &= pixels.DoesPixelMatch(swatchSolidRightX, swatchSolidTopY, swatchColor.IsMatch); + result &= pixels.DoesPixelMatch(swatchSolidLeftX, swatchSolidBottomY, swatchColor.IsMatch); + result &= pixels.DoesPixelMatch(swatchSolidRightX, swatchSolidBottomY, swatchColor.IsMatch); if (!result) { + // Box corners test failed + // WriteLog("Failed to find left edge for potential swatch of color {2}, {3}, {4} at {0}, {1}", x, y, swatch_r, swatch_g, swatch_b); return false; } // scan down the right and left sides - for (int yOff = 1; yOff < (swatchHeight - 5); ++yOff) + for (int yOff = 1; yOff < (swatchSolidHeight - 1); ++yOff) { - int testPixel = solidUpperLeft + (yOff * stride); - result &= IsColorMatch(swatch_r, swatch_r, swatch_r, pixBytes[testPixel], pixBytes[testPixel + 1], pixBytes[testPixel + 2]); - testPixel += ((swatchWidth - 1) * 3); - result &= IsColorMatch(swatch_r, swatch_r, swatch_r, pixBytes[testPixel], pixBytes[testPixel + 1], pixBytes[testPixel + 2]); + result &= pixels.DoesPixelMatch(swatchSolidLeftX, swatchSolidTopY + yOff, swatchColor.IsMatch); + if (!result) + { + pixels.ColorAt(swatchSolidLeftX, swatchSolidTopY + yOff, ref testColor); + break; + } + result &= pixels.DoesPixelMatch(swatchSolidRightX, swatchSolidTopY + yOff, swatchColor.IsMatch); + if (!result) + { + pixels.ColorAt(swatchSolidRightX, swatchSolidTopY + yOff, ref testColor); + } } - if (!result) { - WriteLog("Failed to find left edge for potential swatch of color {2}, {3}, {4} at {0}, {1}", x, y, swatch_r, swatch_g, swatch_b); + WriteLog("Failed to find left/right edges for potential swatch of color {2}, {3}, {4} at {0}, {1} [failed color = {5},{6},{7}]", x, y, swatchColor.r, swatchColor.g, swatchColor.b, testColor.r, testColor.g, testColor.b); + return false; + } + for (int xOff = 1; xOff < (swatchSolidWidth - 1); ++xOff) + { + result &= pixels.DoesPixelMatch(swatchSolidLeftX + xOff, swatchSolidTopY, swatchColor.IsMatch); + if (!result) + { + pixels.ColorAt(swatchSolidLeftX + xOff, swatchSolidTopY, ref testColor); + break; + } + result &= pixels.DoesPixelMatch(swatchSolidLeftX + xOff, swatchSolidBottomY, swatchColor.IsMatch); + if (!result) + { + pixels.ColorAt(swatchSolidLeftX + xOff, swatchSolidBottomY, ref testColor); + break; + } + } + if (!result) + { + WriteLog("Failed to match upper/lower edges for potential swatch of color {2}, {3}, {4} at {0}, {1} [failed color = {5},{6},{7}]", x, y, swatchColor.r, swatchColor.g, swatchColor.b, testColor.r, testColor.g, testColor.b); return false; } - // test the left edge for dark pixels + + // test the left edge for dark pixels -- the bottom-most pixel is bright now int i = 0; - for (i = 0; result && (i < swatchHeight-pixelMultiplier); ++i) + for (i = 1; result && i < swatchHeight - 1; ++i) { - int otherPixelStart = testPixelStart + (stride * i); - result &= IsDarkPixel(pixBytes[otherPixelStart], pixBytes[otherPixelStart+1], pixBytes[otherPixelStart+2]); + result &= pixels.DoesPixelMatch(x, y + i, PixelColor.IsDark); } if (!result) { // No dark border on the left side - WriteLog("Failed to find left border for potential swatch of color {2}, {3}, {4} at {0}, {1}", x, y, swatch_r, swatch_g, swatch_b); + WriteLog("Failed to find left border for potential swatch of color {2}, {3}, {4} at {0}, {1}", x, y, swatchColor.r, swatchColor.g, swatchColor.b); return false; } @@ -365,26 +493,36 @@ namespace DesertPaintLab int papyErrorCount = 0; for (i = 0; result && (i < swatchWidth); ++i) { - int otherPixelStart = testPixelStart + (3 * i); - bool isBorder = IsDarkPixel(pixBytes[otherPixelStart], pixBytes[otherPixelStart+1], pixBytes[otherPixelStart+2]); + bool isBorder = pixels.DoesPixelMatch(x + i, y, PixelColor.IsDark); result &= isBorder; if (!isBorder) { borderError = true; } - if (otherPixelStart >= stride) + + // Checking along the top of the swatch for papyrus + // The row just above is shaded, so check 2 above + if (y > 1) { - otherPixelStart = otherPixelStart - stride; - papyErrorCount += (IsPapyTexture(pixBytes[otherPixelStart], pixBytes[otherPixelStart + 1], pixBytes[otherPixelStart + 2]) ? 0 : 1); + bool isPapyrus = pixels.DoesPixelMatch(x + i, y - 2, PixelColor.IsPapyrus); + papyErrorCount += isPapyrus ? 0 : 1; + } else { - papyErrorCount++; + ++papyErrorCount; } - // Checking along the bottom of the swatch - - otherPixelStart = testPixelStart + (stride * swatchHeight) + (3 * i); - papyErrorCount += (IsPapyTexture(pixBytes[otherPixelStart], pixBytes[otherPixelStart+1], pixBytes[otherPixelStart+2]) ? 0 : 1); + // Checking along the bottom of the swatch for papyrus + if (y < pixels.height) + { + bool isPapyrus = pixels.DoesPixelMatch(x + i, y + swatchHeight, PixelColor.IsPapyrus); + papyErrorCount += isPapyrus ? 0 : 1; + } + else + { + ++papyErrorCount; + } } result &= (papyErrorCount < (swatchWidth / 20)); // allow up to 5% error rate checking for papy texture, because this seems to be inconsistent @@ -399,101 +537,36 @@ namespace DesertPaintLab return result; } - unsafe private bool TestPosition(int x, int y, byte* pixBytes, int screenshotWidth, int screenshotHeight, int stride, ref PaintColor reactedColor, ref int redPixelStart) + unsafe private bool TestPosition(int x, int y, Pixels pixels, ref PaintColor reactedColor, ref int redPixelStart) { - byte pixel_r, pixel_g, pixel_b; - int pixelStart, otherPixelStart; - bool colorMatch = true; - - // Look for the color swatch. - pixelStart = (y * stride) + (x * 3); - pixel_r = pixBytes[pixelStart]; - pixel_g = pixBytes[pixelStart + 1]; - pixel_b = pixBytes[pixelStart + 2]; + PixelColor pixelColor = new PixelColor(); // Check 4 corners of solid area and left/right solid bar areas - bool foundSwatch = IsPossibleSwatchUpperLeft(pixBytes, x, y, stride); // ((pixel_r < 0x46) && (pixel_g < 0x46) && (pixel_b < 0x46)); + bool foundSwatch = IsPossibleSwatchUpperLeft(pixels, x, y); // ((pixel_r < 0x46) && (pixel_g < 0x46) && (pixel_b < 0x46)); if (foundSwatch) { int borderXOffset = 0; - for (borderXOffset = (2 * pixelMultiplier); foundSwatch && (borderXOffset < swatchTestWidth); ++borderXOffset) + for (borderXOffset = 2; foundSwatch && (borderXOffset < swatchTestWidth); ++borderXOffset) { - foundSwatch &= IsPossibleSwatchSlice(pixBytes, x + borderXOffset, y, stride); - foundSwatch &= IsPossibleSwatchSlice(pixBytes, x + swatchWidth - borderXOffset, y, stride); + foundSwatch &= IsPossibleSwatchSlice(pixels, x + borderXOffset, y); + foundSwatch &= IsPossibleSwatchSlice(pixels, x + swatchWidth - borderXOffset, y); } } if (foundSwatch) { - // found a swatch with an appropriate dark top border with papyrus texture above it and papyrus a jump below it - // 4.) Scan the left border of the potential swatch - // location. - colorMatch = true; - for (int i = 2; i < swatchHeight - (2 * pixelMultiplier); ++i) - { - otherPixelStart = pixelStart + (stride * i); - if (!IsColorMatch(pixel_r, pixel_g, pixel_b, pixBytes[otherPixelStart], pixBytes[otherPixelStart + 1], pixBytes[otherPixelStart + 2])) - { - colorMatch = false; - break; - } - } - - if (colorMatch) - { - // WE FOUND THE SWATCH! - // Now we know where the color bars are. - redPixelStart = pixelStart + (redBarSpacing * stride); - int redPixel = redPixelStart; - int redPixelCount = 0; - while ((pixBytes[redPixel] > 0x9F) && - (pixBytes[redPixel + 1] < 0x62) && - (pixBytes[redPixel + 2] < 0x62)) - { - redPixelCount++; - // pixBytes[redPixel] = 0x00; - // pixBytes[redPixel + 1] = 0xFF; - // pixBytes[redPixel + 2] = 0xFF; - redPixel += 3; - } - WriteLog("Color {0}, {1}, {2} is no longer red at {3},{4}", pixBytes[redPixel], pixBytes[redPixel + 1], pixBytes[redPixel + 2], (redPixel % stride)/3, redPixel / stride); + // WE FOUND THE SWATCH! + // Now we know where the color bars are. + int redPixelCount = pixels.LengthOfColorAt(x, y + redBarSpacing, PixelColor.IsRed); + reactedColor.Red = (byte)Math.Round((float)redPixelCount * 255f / (float)colorBarWidth); - reactedColor.Red = (byte)Math.Round((float)redPixelCount * 255f / (float)colorBarWidth); - int greenPixelStart = pixelStart + (greenBarSpacing * stride); - - int greenPixelCount = 0; - while ((pixBytes[greenPixelStart] < 0x62) && - (pixBytes[greenPixelStart + 1] > 0x9F) && - (pixBytes[greenPixelStart + 2] < 0x62)) - { - greenPixelCount++; - // pixBytes[greenPixelStart] = 0x00; - // pixBytes[greenPixelStart + 1] = 0xFF; - // pixBytes[greenPixelStart + 2] = 0xFF; - greenPixelStart += 3; - } - WriteLog("Color {0}, {1}, {2} is no longer green at pixel offset {3},{4}", pixBytes[greenPixelStart], pixBytes[greenPixelStart + 1], pixBytes[greenPixelStart + 2], (greenPixelStart % stride)/3, greenPixelStart / stride); + int greenPixelCount = pixels.LengthOfColorAt(x, y + greenBarSpacing, PixelColor.IsGreen); + reactedColor.Green = (byte)Math.Round((float)greenPixelCount * 255f / (float)colorBarWidth); - reactedColor.Green = (byte)Math.Round((float)greenPixelCount * 255f / (float)colorBarWidth); - int bluePixelStart = pixelStart + (blueBarSpacing * stride); - - int bluePixelCount = 0; - while ((pixBytes[bluePixelStart] < 0x62) && - (pixBytes[bluePixelStart + 1] < 0x62) && - (pixBytes[bluePixelStart + 2] > 0x9F)) - { - bluePixelCount++; - // pixBytes[bluePixelStart] = 0x00; - // pixBytes[bluePixelStart + 1] = 0xFF; - // pixBytes[bluePixelStart + 2] = 0xFF; - bluePixelStart += 3; - } - WriteLog("Color {0}, {1}, {2} is no longer blue at pixel offset {3},{4}", pixBytes[bluePixelStart], pixBytes[bluePixelStart + 1], pixBytes[bluePixelStart + 2], (bluePixelStart % stride)/3, bluePixelStart / stride); - - reactedColor.Blue = (byte)Math.Round((float)bluePixelCount * 255f / (float)colorBarWidth); - WriteLog("Found the color swatch at {0}, {1}. Color={2} Red={3}px Green={4}px Blue={5}px", x, y, reactedColor, redPixelCount, greenPixelCount, bluePixelCount); - return true; - } + int bluePixelCount = pixels.LengthOfColorAt(x, y + blueBarSpacing, PixelColor.IsBlue); + reactedColor.Blue = (byte)Math.Round((float)bluePixelCount * 255f / (float)colorBarWidth); + WriteLog("Found the color swatch at {0}, {1}. Color={2} Red={3}px Green={4}px Blue={5}px", x, y, reactedColor, redPixelCount, greenPixelCount, bluePixelCount); + return true; } return false; } @@ -507,11 +580,19 @@ namespace DesertPaintLab IsCaptured = false; _recordedColor.Clear(); + Pixels pixels = new Pixels + { + pixBytes = pixBytes, + width = screenshotWidth, + height = screenshotHeight, + stride = stride, + pixelMultiplier = pixelMultiplier + }; IsCaptured = false; if (!firstRun) { // If this is not the first run, let's check the last location, to see if the UI is still there. - if (TestPosition(lastSwatchX, lastSwatchY, pixBytes, screenshotWidth, screenshotHeight, stride, ref reactedColor, ref redPixelStart)) + if (TestPosition(lastSwatchX, lastSwatchY, pixels, ref reactedColor, ref redPixelStart)) { IsCaptured = true; RedBarX = (redPixelStart % stride) / 3; @@ -525,22 +606,44 @@ namespace DesertPaintLab } } - for (X = 0; X < screenshotWidth - colorBarWidth; ++X) + int startX; + int endX; + int startY; + int endY; + DesertPaintLab.AppSettings.Get("ScanOffsetX", out startX); + DesertPaintLab.AppSettings.Get("ScanOffsetY", out startY); + DesertPaintLab.AppSettings.Get("ScanLimitX", out endX); + DesertPaintLab.AppSettings.Get("ScanLimitY", out endY); + if (endX == 0) endX = screenshotWidth; + if (endY == 0) endY = screenshotHeight; + + int patchTestSize = (swatchHeight / 2) - 1; + PixelColor patchColor = new PixelColor(); + for (int roughX = startX; roughX < endX - colorBarWidth - patchTestSize; roughX += patchTestSize) { - for (int Y = 0; Y < (screenshotHeight - (blueBarSpacing + 10) /*53*/); ++Y) + for (int roughY = startY; roughY < (endY - (blueBarSpacing + 10) - patchTestSize /*53*/); roughY += patchTestSize) { - if (TestPosition(X, Y, pixBytes, screenshotWidth, screenshotHeight, stride, ref reactedColor, ref redPixelStart)) + OnCaptureProgress?.Invoke(roughX, roughY); + if (!pixels.IsSolidPatchAt(roughX, roughY, patchTestSize, patchTestSize)) continue; + pixels.ColorAt(roughX, roughY, ref patchColor); + for (X = roughX - patchTestSize; X < roughX; ++X) { - RedBarX = (redPixelStart % stride) / 3; - RedBarY = redPixelStart / stride; - RecordedColor = reactedColor; - lastSwatchX = X; - lastSwatchY = Y; - firstRun = false; - IsCaptured = true; - return true; + for (Y = roughY - patchTestSize; Y < roughY; ++Y) + { + if (TestPosition(X, Y, pixels, ref reactedColor, ref redPixelStart)) + { + RedBarX = (redPixelStart % stride) / 3; + RedBarY = redPixelStart / stride; + RecordedColor = reactedColor; + lastSwatchX = X; + lastSwatchY = Y; + firstRun = false; + IsCaptured = true; + return true; + } + } } - OnCaptureProgress?.Invoke(X, Y); + System.Console.WriteLine("False-positive patch of color {0},{1},{2} at {3},{4}", patchColor.r, patchColor.g, patchColor.b, roughX, roughY); } } return false;