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;